lua_struct.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. use std::ffi::c_void;
  2. use std::io::{self, Cursor};
  3. use std::io::{Read, Seek, Write};
  4. use std::os::raw::c_uint;
  5. use lua::lua_State;
  6. use lua_shared as lua;
  7. use crate::check_slice;
  8. #[derive(Debug)]
  9. enum KOption {
  10. I8,
  11. U8,
  12. I16,
  13. U16,
  14. I32,
  15. U32,
  16. Usize,
  17. Float,
  18. Double,
  19. Char,
  20. String,
  21. NOP,
  22. }
  23. enum Endianness {
  24. Little,
  25. Big,
  26. Native,
  27. }
  28. struct ReaderState<'a> {
  29. state: *mut c_void,
  30. endianness: Endianness,
  31. fmt: &'a [u8],
  32. }
  33. fn is_digit(byte: u8) -> bool {
  34. byte ^ b'0' < 10
  35. }
  36. unsafe fn read_number(state: &mut ReaderState) -> Option<usize> {
  37. if state.fmt.len() == 0 || !is_digit(state.fmt[0]) {
  38. return None;
  39. }
  40. let mut result = 0;
  41. while state.fmt.len() > 0 && is_digit(state.fmt[0]) {
  42. let (opt, rest) = state.fmt.split_at(1);
  43. state.fmt = rest;
  44. result = result * 10 + (opt[0] - b'0') as usize;
  45. }
  46. Some(result)
  47. }
  48. #[derive(Debug)]
  49. pub enum StructError {
  50. Error(*const u8),
  51. ArgError(i32, *const u8),
  52. InvalidFormatOption(*const u8, c_uint),
  53. IOError(io::Error),
  54. }
  55. impl From<std::io::Error> for StructError {
  56. fn from(e: std::io::Error) -> Self {
  57. StructError::IOError(e)
  58. }
  59. }
  60. unsafe fn get_option(state: &mut ReaderState) -> Result<Option<(KOption, usize)>, StructError> {
  61. if state.fmt.len() == 0 {
  62. return Ok(None);
  63. }
  64. let (opt, rest) = state.fmt.split_at(1);
  65. state.fmt = rest;
  66. match opt[0] {
  67. b'b' => Ok(Some((KOption::I8, std::mem::size_of::<i8>()))),
  68. b'B' => Ok(Some((KOption::U8, std::mem::size_of::<u8>()))),
  69. b'h' => Ok(Some((KOption::I16, std::mem::size_of::<i16>()))),
  70. b'H' => Ok(Some((KOption::U16, std::mem::size_of::<u16>()))),
  71. b'l' => Ok(Some((KOption::I32, std::mem::size_of::<i32>()))),
  72. b'L' => Ok(Some((KOption::U32, std::mem::size_of::<u32>()))),
  73. b'T' => Ok(Some((KOption::Usize, std::mem::size_of::<usize>()))),
  74. b'f' => Ok(Some((KOption::Float, std::mem::size_of::<f32>()))),
  75. b'd' => Ok(Some((KOption::Double, std::mem::size_of::<f64>()))),
  76. b'n' => Ok(Some((KOption::Double, std::mem::size_of::<f64>()))),
  77. b's' => Ok(Some((KOption::String, std::mem::size_of::<u16>()))),
  78. b'c' => match read_number(state) {
  79. Some(len) => Ok(Some((KOption::Char, len))),
  80. None => Err(StructError::Error(lua::cstr!(
  81. "missing size for format option 'c'"
  82. ))),
  83. },
  84. b' ' => Ok(Some((KOption::NOP, 0))),
  85. b'<' => {
  86. state.endianness = Endianness::Little;
  87. Ok(Some((KOption::NOP, 0)))
  88. }
  89. b'>' => {
  90. state.endianness = Endianness::Big;
  91. Ok(Some((KOption::NOP, 0)))
  92. }
  93. b'=' => {
  94. state.endianness = Endianness::Native;
  95. Ok(Some((KOption::NOP, 0)))
  96. }
  97. token @ _ => Err(StructError::InvalidFormatOption(
  98. lua::cstr!("invalid format option '%c'"),
  99. token as c_uint,
  100. )),
  101. }
  102. }
  103. // I don't fucking care. luaL_Buffer is allocated on the stack.
  104. // At the same time, this buffer is two times bigger than lua's string buffer.
  105. static mut STRING_BUFFER: [u8; 65536] = [0; 65536];
  106. macro_rules! pack_number {
  107. ($state:ident, $buffer:ident, $value:tt) => {
  108. match $state.endianness {
  109. Endianness::Little => $buffer.write(&$value.to_le_bytes())?,
  110. Endianness::Big => $buffer.write(&$value.to_be_bytes())?,
  111. Endianness::Native => $buffer.write(&$value.to_ne_bytes())?,
  112. }
  113. };
  114. }
  115. macro_rules! unpack_number {
  116. ($state:ident, $buffer:ident, $typ:ty) => {{
  117. let mut data = [0; std::mem::size_of::<$typ>()];
  118. $buffer.read(&mut data)?;
  119. match $state.endianness {
  120. Endianness::Little => <$typ>::from_le_bytes(data),
  121. Endianness::Big => <$typ>::from_be_bytes(data),
  122. Endianness::Native => <$typ>::from_ne_bytes(data),
  123. }
  124. }};
  125. }
  126. pub fn pack(state: lua_State, fmt: &[u8], start: i32) -> Result<&'static [u8], StructError> {
  127. unsafe {
  128. let mut arg = start;
  129. let mut reader_state = ReaderState {
  130. state: state,
  131. endianness: Endianness::Native,
  132. fmt: fmt,
  133. };
  134. let mut buffer = Cursor::new(&mut STRING_BUFFER[..]);
  135. while let Some((option, size)) = get_option(&mut reader_state)? {
  136. if let KOption::NOP = option {
  137. continue;
  138. }
  139. if STRING_BUFFER.len() - (buffer.seek(std::io::SeekFrom::Current(0))? as usize) < size {
  140. return Err(StructError::Error(lua::cstr!("buffer overflow")));
  141. }
  142. match option {
  143. KOption::I8 => {
  144. let value = lua::Lcheckinteger(reader_state.state, arg) as i8;
  145. pack_number!(reader_state, buffer, value);
  146. }
  147. KOption::U8 => {
  148. let value = lua::Lcheckinteger(reader_state.state, arg) as u8;
  149. pack_number!(reader_state, buffer, value);
  150. }
  151. KOption::I16 => {
  152. let value = lua::Lcheckinteger(reader_state.state, arg) as i16;
  153. pack_number!(reader_state, buffer, value);
  154. }
  155. KOption::U16 => {
  156. let value = lua::Lcheckinteger(reader_state.state, arg) as u16;
  157. pack_number!(reader_state, buffer, value);
  158. }
  159. KOption::I32 => {
  160. let value = lua::Lcheckinteger(reader_state.state, arg) as i32;
  161. pack_number!(reader_state, buffer, value);
  162. }
  163. KOption::U32 => {
  164. let value = lua::Lcheckinteger(reader_state.state, arg) as u32;
  165. pack_number!(reader_state, buffer, value);
  166. }
  167. KOption::Usize => {
  168. let value = lua::Lcheckinteger(reader_state.state, arg) as usize;
  169. pack_number!(reader_state, buffer, value);
  170. }
  171. KOption::Float => {
  172. let value = lua::Lchecknumber(reader_state.state, arg) as f32;
  173. pack_number!(reader_state, buffer, value);
  174. }
  175. KOption::Double => {
  176. let value = lua::Lchecknumber(reader_state.state, arg);
  177. pack_number!(reader_state, buffer, value);
  178. }
  179. KOption::Char => {
  180. let str = check_slice!(state, arg);
  181. if str.len() >= size {
  182. buffer.write(&str[..size])?;
  183. } else {
  184. buffer.write(str)?;
  185. for _ in 0..size - str.len() {
  186. buffer.write(&[0])?;
  187. }
  188. }
  189. }
  190. KOption::String => {
  191. let str = check_slice!(state, arg);
  192. if (str.len() > 1 << 16 - 2)
  193. || ((buffer.seek(std::io::SeekFrom::Current(0))? as usize) + size
  194. > STRING_BUFFER.len())
  195. {
  196. return Err(StructError::ArgError(
  197. arg,
  198. lua::cstr!("string won't fit in the buffer"),
  199. ));
  200. }
  201. pack_number!(reader_state, buffer, (str.len() as u16));
  202. buffer.write(str)?;
  203. }
  204. KOption::NOP => {}
  205. }
  206. arg += 1;
  207. }
  208. Ok(&STRING_BUFFER[..(buffer.seek(std::io::SeekFrom::Current(0))? as usize)])
  209. }
  210. }
  211. pub fn unpack(state: lua_State, fmt: &[u8], data: &[u8]) -> Result<i32, StructError> {
  212. unsafe {
  213. let mut nrets = 0;
  214. let mut reader_state = ReaderState {
  215. state: state,
  216. endianness: Endianness::Native,
  217. fmt: fmt,
  218. };
  219. let mut buffer = Cursor::new(data);
  220. while let Some((option, size)) = get_option(&mut reader_state)? {
  221. if let KOption::NOP = option {
  222. continue;
  223. }
  224. if data.len() - (buffer.seek(std::io::SeekFrom::Current(0))? as usize) < size {
  225. return Err(StructError::Error(lua::cstr!("data string too short")));
  226. }
  227. nrets += 1;
  228. match option {
  229. KOption::I8 => {
  230. let value = unpack_number!(reader_state, buffer, i8);
  231. lua::pushinteger(state, value as _);
  232. }
  233. KOption::U8 => {
  234. let value = unpack_number!(reader_state, buffer, u8);
  235. lua::pushinteger(state, value as _);
  236. }
  237. KOption::I16 => {
  238. let value = unpack_number!(reader_state, buffer, i16);
  239. lua::pushinteger(state, value as _);
  240. }
  241. KOption::U16 => {
  242. let value = unpack_number!(reader_state, buffer, u16);
  243. lua::pushinteger(state, value as _);
  244. }
  245. KOption::I32 => {
  246. let value = unpack_number!(reader_state, buffer, i32);
  247. lua::pushinteger(state, value as _);
  248. }
  249. KOption::U32 => {
  250. let value = unpack_number!(reader_state, buffer, u32);
  251. lua::pushinteger(state, value as _);
  252. }
  253. KOption::Usize => {
  254. let value = unpack_number!(reader_state, buffer, usize);
  255. lua::pushinteger(state, value as _);
  256. }
  257. KOption::Float => {
  258. let value = unpack_number!(reader_state, buffer, f32);
  259. lua::pushnumber(state, value as _);
  260. }
  261. KOption::Double => {
  262. let value = unpack_number!(reader_state, buffer, f64);
  263. lua::pushnumber(state, value);
  264. }
  265. KOption::Char => {
  266. let offset = buffer.seek(std::io::SeekFrom::Current(0))? as usize;
  267. lua::pushlstring(state, data.as_ptr().add(offset), size);
  268. }
  269. KOption::String => {
  270. let value = unpack_number!(reader_state, buffer, u16);
  271. let offset = buffer.seek(std::io::SeekFrom::Current(0))? as usize;
  272. if offset + value as usize > data.len() {
  273. return Err(StructError::Error(lua::cstr!("data string too short")));
  274. }
  275. buffer.seek(std::io::SeekFrom::Current(value as _))?;
  276. lua::pushlstring(state, data.as_ptr().add(offset), value as _);
  277. }
  278. KOption::NOP => {}
  279. }
  280. }
  281. Ok(nrets)
  282. }
  283. }