diff options
| -rw-r--r-- | c++-libbu++/src/gatsc/main.cpp | 20 | ||||
| -rw-r--r-- | c++-libbu++/src/protocolgats.cpp | 1 | ||||
| -rw-r--r-- | c++-qt/.gitignore | 2 | ||||
| -rw-r--r-- | rust/.gitignore | 1 | ||||
| -rw-r--r-- | rust/Cargo.lock | 32 | ||||
| -rw-r--r-- | rust/Cargo.toml | 10 | ||||
| -rw-r--r-- | rust/src/lib.rs | 797 |
7 files changed, 862 insertions, 1 deletions
diff --git a/c++-libbu++/src/gatsc/main.cpp b/c++-libbu++/src/gatsc/main.cpp index c95b914..334131c 100644 --- a/c++-libbu++/src/gatsc/main.cpp +++ b/c++-libbu++/src/gatsc/main.cpp | |||
| @@ -24,6 +24,7 @@ public: | |||
| 24 | Options( int argc, char *argv[] ) : | 24 | Options( int argc, char *argv[] ) : |
| 25 | bCompile( true ), | 25 | bCompile( true ), |
| 26 | bCompress( false ), | 26 | bCompress( false ), |
| 27 | bPrint( false ), | ||
| 27 | bHex( false ) | 28 | bHex( false ) |
| 28 | { | 29 | { |
| 29 | addHelpBanner("Gats Compiler\nUsage: gatsc [options] [input]\n"); | 30 | addHelpBanner("Gats Compiler\nUsage: gatsc [options] [input]\n"); |
| @@ -33,6 +34,8 @@ public: | |||
| 33 | 34 | ||
| 34 | addOption( bCompile, 'd', "decompile", | 35 | addOption( bCompile, 'd', "decompile", |
| 35 | "Convert binary gats to text gats."); | 36 | "Convert binary gats to text gats."); |
| 37 | addOption( bPrint, 'p', "print", | ||
| 38 | "Print the structure of the input to stdout."); | ||
| 36 | 39 | ||
| 37 | addOption( bCompress, 'z', "compress", "Compress with deflate."); | 40 | addOption( bCompress, 'z', "compress", "Compress with deflate."); |
| 38 | addOption( bHex, 'x', "hex", "Encode output as hex."); | 41 | addOption( bHex, 'x', "hex", "Encode output as hex."); |
| @@ -43,6 +46,7 @@ public: | |||
| 43 | 46 | ||
| 44 | setOverride("decompile", false ); | 47 | setOverride("decompile", false ); |
| 45 | setOverride("compress", true ); | 48 | setOverride("compress", true ); |
| 49 | setOverride("print", true ); | ||
| 46 | setOverride("hex", true ); | 50 | setOverride("hex", true ); |
| 47 | 51 | ||
| 48 | parse( argc, argv ); | 52 | parse( argc, argv ); |
| @@ -56,6 +60,7 @@ public: | |||
| 56 | 60 | ||
| 57 | bool bCompile; | 61 | bool bCompile; |
| 58 | bool bCompress; | 62 | bool bCompress; |
| 63 | bool bPrint; | ||
| 59 | bool bHex; | 64 | bool bHex; |
| 60 | String sInput; | 65 | String sInput; |
| 61 | String sOutput; | 66 | String sOutput; |
| @@ -71,6 +76,21 @@ int main( int argc, char *argv[] ) | |||
| 71 | return 1; | 76 | return 1; |
| 72 | } | 77 | } |
| 73 | 78 | ||
| 79 | if( opt.bPrint ) | ||
| 80 | { | ||
| 81 | File fIn( opt.sInput, File::Read ); | ||
| 82 | Gats::GatsStream gs( fIn ); | ||
| 83 | Gats::Object *pObj = gs.readObject(); | ||
| 84 | if( pObj == NULL ) | ||
| 85 | { | ||
| 86 | println("Error reading input."); | ||
| 87 | return -1; | ||
| 88 | } | ||
| 89 | sio << *pObj << sio.nl << sio.nl; | ||
| 90 | delete pObj; | ||
| 91 | return -1; | ||
| 92 | } | ||
| 93 | |||
| 74 | if( opt.sOutput.isEmpty() ) | 94 | if( opt.sOutput.isEmpty() ) |
| 75 | { | 95 | { |
| 76 | opt.sOutput.set( opt.sInput.begin(), opt.sInput.find('.') ); | 96 | opt.sOutput.set( opt.sInput.begin(), opt.sInput.find('.') ); |
diff --git a/c++-libbu++/src/protocolgats.cpp b/c++-libbu++/src/protocolgats.cpp index 2430100..bc8f0c5 100644 --- a/c++-libbu++/src/protocolgats.cpp +++ b/c++-libbu++/src/protocolgats.cpp | |||
| @@ -59,5 +59,6 @@ void Gats::ProtocolGats::onNewData( Bu::Client *pClient ) | |||
| 59 | void Gats::ProtocolGats::writeObject( Gats::Object *pObj ) | 59 | void Gats::ProtocolGats::writeObject( Gats::Object *pObj ) |
| 60 | { | 60 | { |
| 61 | pStream->writeObject( pObj ); | 61 | pStream->writeObject( pObj ); |
| 62 | pUsedClient->flush(); | ||
| 62 | } | 63 | } |
| 63 | 64 | ||
diff --git a/c++-qt/.gitignore b/c++-qt/.gitignore index 507c4e5..1bbdf56 100644 --- a/c++-qt/.gitignore +++ b/c++-qt/.gitignore | |||
| @@ -3,4 +3,4 @@ object_script.* | |||
| 3 | release | 3 | release |
| 4 | debug | 4 | debug |
| 5 | tmp | 5 | tmp |
| 6 | 6 | .qmake.stash | |
diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/rust/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| /target | |||
diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..3c89b5e --- /dev/null +++ b/rust/Cargo.lock | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | # This file is automatically @generated by Cargo. | ||
| 2 | # It is not intended for manual editing. | ||
| 3 | version = 4 | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "bytes" | ||
| 7 | version = "1.11.0" | ||
| 8 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 9 | checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" | ||
| 10 | |||
| 11 | [[package]] | ||
| 12 | name = "gats" | ||
| 13 | version = "0.1.0" | ||
| 14 | dependencies = [ | ||
| 15 | "tokio", | ||
| 16 | ] | ||
| 17 | |||
| 18 | [[package]] | ||
| 19 | name = "pin-project-lite" | ||
| 20 | version = "0.2.16" | ||
| 21 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 22 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" | ||
| 23 | |||
| 24 | [[package]] | ||
| 25 | name = "tokio" | ||
| 26 | version = "1.48.0" | ||
| 27 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| 28 | checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" | ||
| 29 | dependencies = [ | ||
| 30 | "bytes", | ||
| 31 | "pin-project-lite", | ||
| 32 | ] | ||
diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..e945f0b --- /dev/null +++ b/rust/Cargo.toml | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | [package] | ||
| 2 | name = "gats" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2024" | ||
| 5 | |||
| 6 | [dependencies] | ||
| 7 | tokio = { version = "1.48.0", optional = true, features = ["io-util"] } | ||
| 8 | |||
| 9 | [features] | ||
| 10 | tokio = ["dep:tokio"] | ||
diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..213fa39 --- /dev/null +++ b/rust/src/lib.rs | |||
| @@ -0,0 +1,797 @@ | |||
| 1 | //! GATS - Generalized Agile Transport System | ||
| 2 | //! | ||
| 3 | //! This is a format designed to pack complex data structures into compact and | ||
| 4 | //! easily transmissible packets that are platform independent. This format | ||
| 5 | //! supports integers, byte arrays, booleans, lists, dictionaries (with string | ||
| 6 | //! keys), floats, and null. | ||
| 7 | //! | ||
| 8 | //! Integers are stored in a packed format that requires only as many bytes as | ||
| 9 | //! it takes to represent the value. This generally results in a reduction of | ||
| 10 | //! space used, and rarely an increase. | ||
| 11 | //! | ||
| 12 | //! Floating point numbers are architecture independent and stored in a similar | ||
| 13 | //! packed format to present them as accurately as possible in the minimal | ||
| 14 | //! amount of space. | ||
| 15 | //! | ||
| 16 | //! In theory, both the integer and floating point formats are unbound and could | ||
| 17 | //! represent values with any number of bytes, but in reality most | ||
| 18 | //! implementations limit this to 64 bits. | ||
| 19 | //! | ||
| 20 | //! For easy transmission each Gats Value can be serialized as a packet, which | ||
| 21 | //! contains a version header and a total size which makes it easier to read | ||
| 22 | //! from a socket or the like. Each packet can only contain one value, but | ||
| 23 | //! values can be complex types. | ||
| 24 | |||
| 25 | use std::collections::HashMap; | ||
| 26 | use std::io::{Cursor, Error, ErrorKind, Read, Write}; | ||
| 27 | use std::num::FpCategory; | ||
| 28 | use std::sync::LazyLock; | ||
| 29 | use std::vec::Vec; | ||
| 30 | |||
| 31 | #[cfg(feature = "tokio")] | ||
| 32 | use tokio::io::{AsyncReadExt,AsyncWriteExt,AsyncRead,AsyncWrite}; | ||
| 33 | |||
| 34 | /// Represents a value in a Gats structure. | ||
| 35 | #[derive(PartialEq,Debug)] | ||
| 36 | pub enum Value { | ||
| 37 | Integer(i64), | ||
| 38 | ByteString(Vec<u8>), | ||
| 39 | Boolean(bool), | ||
| 40 | List(Vec<Value>), | ||
| 41 | Dictionary(HashMap<String, Value>), | ||
| 42 | Float(f64), | ||
| 43 | Null, | ||
| 44 | } | ||
| 45 | |||
| 46 | /// Used by the Value::bytes_to_read function to communicate with the caller | ||
| 47 | /// about their buffer so far. If Estimate is returned then you should | ||
| 48 | /// reallocate, read data, and try again. If final is returned then there is | ||
| 49 | /// no reason to call the estimate any more, you can just be done once you've | ||
| 50 | /// resized and filled the buffer. | ||
| 51 | pub enum BytesToRead { | ||
| 52 | Estimate(usize), | ||
| 53 | Final(usize), | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Used by the parser internally to handle values in the format that are not | ||
| 57 | /// Values but are needed to parse the data. | ||
| 58 | enum ValueParse { | ||
| 59 | Value(Value), | ||
| 60 | EndMarker, | ||
| 61 | } | ||
| 62 | |||
| 63 | impl PartialEq<i64> for Value { | ||
| 64 | fn eq(&self, other: &i64) -> bool { | ||
| 65 | if let Value::Integer(x) = self { | ||
| 66 | x == other | ||
| 67 | } else { | ||
| 68 | false | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | impl PartialEq<Vec<u8>> for Value { | ||
| 74 | fn eq(&self, other: &Vec<u8>) -> bool { | ||
| 75 | if let Value::ByteString(x) = self { | ||
| 76 | x == other | ||
| 77 | } else { | ||
| 78 | false | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | impl PartialEq<[u8]> for Value { | ||
| 84 | fn eq(&self, other: &[u8]) -> bool { | ||
| 85 | if let Value::ByteString(x) = self { | ||
| 86 | x == other | ||
| 87 | } else { | ||
| 88 | false | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | impl PartialEq<bool> for Value { | ||
| 94 | fn eq(&self, other: &bool) -> bool { | ||
| 95 | if let Value::Boolean(x) = self { | ||
| 96 | x == other | ||
| 97 | } else { | ||
| 98 | false | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | impl PartialEq<Vec<Value>> for Value { | ||
| 104 | fn eq(&self, other: &Vec<Value>) -> bool { | ||
| 105 | if let Value::List(x) = self { | ||
| 106 | x == other | ||
| 107 | } else { | ||
| 108 | false | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | impl PartialEq<HashMap<String, Value>> for Value { | ||
| 114 | fn eq(&self, other: &HashMap<String, Value>) -> bool { | ||
| 115 | if let Value::Dictionary(x) = self { | ||
| 116 | x == other | ||
| 117 | } else { | ||
| 118 | false | ||
| 119 | } | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | impl PartialEq<f64> for Value { | ||
| 124 | fn eq(&self, other: &f64) -> bool { | ||
| 125 | if let Value::Float(x) = self { | ||
| 126 | x == other | ||
| 127 | } else { | ||
| 128 | false | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | impl std::cmp::Eq for Value {} | ||
| 134 | |||
| 135 | /// Used for converting floating point numbers to their packed format, | ||
| 136 | /// this isn't a number we can store easily as a literal without a hex format. | ||
| 137 | static FLOAT_DIVISOR: LazyLock<f64> = LazyLock::new(|| (256_f64).ln()); | ||
| 138 | |||
| 139 | /// Internal helper to convert a number into a packed format. | ||
| 140 | fn write_packed_int(x: i64) -> Vec<u8> { | ||
| 141 | let mut w = Vec::<u8>::with_capacity(10); | ||
| 142 | let mut v = x; | ||
| 143 | let mut bb = if x < 0 { | ||
| 144 | v = -v; | ||
| 145 | 0x40_u8 | ||
| 146 | } else { | ||
| 147 | 0x00_u8 | ||
| 148 | } | (v & 0x3F) as u8 | ||
| 149 | | if v > (v & 0x3F) { 0x80_u8 } else { 0x00_u8 }; | ||
| 150 | w.push(bb); | ||
| 151 | v >>= 6; | ||
| 152 | while v > 0 { | ||
| 153 | bb = (v & 0x7F) as u8 | if v > (v & 0x7F) { 0x80_u8 } else { 0x00_u8 }; | ||
| 154 | w.push(bb); | ||
| 155 | v >>= 7; | ||
| 156 | } | ||
| 157 | |||
| 158 | w | ||
| 159 | } | ||
| 160 | |||
| 161 | /// Internal helper to convert a buffer containing a packed integer back into | ||
| 162 | /// an integer. Any extra data will be ignored. | ||
| 163 | fn read_packed_int<R: Read>(r: &mut R) -> Result<i64, Error> { | ||
| 164 | let mut out: i64; | ||
| 165 | let mut bb = [0u8]; | ||
| 166 | r.read_exact(&mut bb)?; | ||
| 167 | let negative = (bb[0] & 0x40_u8) == 0x40_u8; | ||
| 168 | out = (bb[0] & 0x3F_u8) as i64; | ||
| 169 | let mut c = 0; | ||
| 170 | while (bb[0] & 0x80_u8) != 0 { | ||
| 171 | r.read_exact(&mut bb)?; | ||
| 172 | out |= ((bb[0] & 0x7F_u8) as i64) << (6 + 7 * c); | ||
| 173 | c += 1; | ||
| 174 | } | ||
| 175 | |||
| 176 | if negative { Ok(-out) } else { Ok(out) } | ||
| 177 | } | ||
| 178 | |||
| 179 | impl Value { | ||
| 180 | pub fn new_int( value: i64 ) -> Value { | ||
| 181 | Value::Integer( value ) | ||
| 182 | } | ||
| 183 | |||
| 184 | pub fn new_byte_string( data: &[u8] ) -> Value { | ||
| 185 | Value::ByteString( data.to_vec() ) | ||
| 186 | } | ||
| 187 | |||
| 188 | pub fn new_bool( value: bool ) -> Value { | ||
| 189 | Value::Boolean( value ) | ||
| 190 | } | ||
| 191 | |||
| 192 | pub fn new_list() -> Value { | ||
| 193 | Value::List( Vec::new() ) | ||
| 194 | } | ||
| 195 | |||
| 196 | pub fn new_dict() -> Value { | ||
| 197 | Value::Dictionary(HashMap::new()) | ||
| 198 | } | ||
| 199 | |||
| 200 | pub fn dict_from<const N: usize>( data: [(String,Value); N] ) -> Value { | ||
| 201 | Value::Dictionary(HashMap::from( data )) | ||
| 202 | } | ||
| 203 | |||
| 204 | pub fn new_float( value: f64 ) -> Value { | ||
| 205 | Value::Float( value ) | ||
| 206 | } | ||
| 207 | |||
| 208 | pub fn new_null() -> Value { | ||
| 209 | Value::Null | ||
| 210 | } | ||
| 211 | |||
| 212 | pub fn is_null(&self) -> bool { | ||
| 213 | matches!(self, Value::Null) | ||
| 214 | } | ||
| 215 | |||
| 216 | pub fn get(&self, key: &str) -> Option<&Value> { | ||
| 217 | if let Value::Dictionary(d) = self { | ||
| 218 | d.get( key ) | ||
| 219 | } else { | ||
| 220 | None | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | pub fn as_bool( &self ) -> Result<bool,Error> { | ||
| 225 | if let Value::Boolean(d) = self { | ||
| 226 | Ok(*d) | ||
| 227 | } else { | ||
| 228 | Err(Error::new( | ||
| 229 | ErrorKind::InvalidData, | ||
| 230 | "as_bool called on non boolean value.", | ||
| 231 | )) | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | pub fn as_byte_string( &self ) -> Result<&Vec<u8>,Error> { | ||
| 236 | if let Value::ByteString(d) = self { | ||
| 237 | Ok(&d) | ||
| 238 | } else { | ||
| 239 | Err(Error::new( | ||
| 240 | ErrorKind::InvalidData, | ||
| 241 | "as_byte_string called on non ByteString value.", | ||
| 242 | )) | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | pub fn as_int( &self ) -> Result<i64,Error> { | ||
| 247 | if let Value::Integer(i) = self { | ||
| 248 | Ok(*i) | ||
| 249 | } else { | ||
| 250 | Err(Error::new( | ||
| 251 | ErrorKind::InvalidData, | ||
| 252 | "as_int called on non Integer value.", | ||
| 253 | )) | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | pub fn write<W: Write>(&self, w: &mut W) -> Result<(), Error> { | ||
| 258 | match self { | ||
| 259 | Self::Integer(x) => { | ||
| 260 | let bb = [b'i']; | ||
| 261 | w.write_all(&bb)?; | ||
| 262 | w.write_all(&write_packed_int(*x))?; | ||
| 263 | } | ||
| 264 | Self::ByteString(s) => { | ||
| 265 | let bb = [b's']; | ||
| 266 | w.write_all(&bb)?; | ||
| 267 | w.write_all(&write_packed_int(s.len() as i64))?; | ||
| 268 | w.write_all(s)?; | ||
| 269 | } | ||
| 270 | Self::Boolean(b) => { | ||
| 271 | let mut bb = [0u8]; | ||
| 272 | bb[0] = if *b { b'1' } else { b'0' }; | ||
| 273 | w.write_all(&bb)?; | ||
| 274 | } | ||
| 275 | Self::List(l) => { | ||
| 276 | let mut bb = [b'l']; | ||
| 277 | w.write_all(&bb)?; | ||
| 278 | for v in l { | ||
| 279 | v.write(w)?; | ||
| 280 | } | ||
| 281 | bb[0] = b'e'; | ||
| 282 | w.write_all(&bb)?; | ||
| 283 | } | ||
| 284 | Self::Dictionary(d) => { | ||
| 285 | let mut bb = [b'd']; | ||
| 286 | w.write_all(&bb)?; | ||
| 287 | for (k, v) in d { | ||
| 288 | (Value::ByteString(k.clone().into_bytes())).write(w)?; | ||
| 289 | v.write(w)?; | ||
| 290 | } | ||
| 291 | bb[0] = b'e'; | ||
| 292 | w.write_all(&bb)?; | ||
| 293 | } | ||
| 294 | Self::Float(f) => { | ||
| 295 | match f.classify() { | ||
| 296 | FpCategory::Nan => { | ||
| 297 | let mut bb = [b'F', 0u8]; | ||
| 298 | bb[1] = if f.is_sign_negative() { 'N' } else { 'n' } as u8; | ||
| 299 | w.write_all(&bb)?; | ||
| 300 | } | ||
| 301 | FpCategory::Infinite => { | ||
| 302 | let mut bb = [b'F', 0u8]; | ||
| 303 | bb[1] = if f.is_sign_negative() { 'I' } else { 'i' } as u8; | ||
| 304 | w.write_all(&bb)?; | ||
| 305 | } | ||
| 306 | FpCategory::Zero => { | ||
| 307 | let mut bb = [b'F', 0u8]; | ||
| 308 | bb[1] = if f.is_sign_negative() { 'Z' } else { 'z' } as u8; | ||
| 309 | w.write_all(&bb)?; | ||
| 310 | } | ||
| 311 | FpCategory::Subnormal => { | ||
| 312 | let mut bb = [b'F', 0u8]; | ||
| 313 | // The format doesn't account for these...uh...make them zero? | ||
| 314 | bb[1] = if f.is_sign_negative() { 'Z' } else { 'z' } as u8; | ||
| 315 | w.write_all(&bb)?; | ||
| 316 | } | ||
| 317 | FpCategory::Normal => { | ||
| 318 | let bb = [b'f']; | ||
| 319 | w.write_all(&bb)?; | ||
| 320 | let mut bin = Vec::<u8>::with_capacity(10); | ||
| 321 | let (negative, d) = if f.is_sign_negative() { | ||
| 322 | (true, -*f) | ||
| 323 | } else { | ||
| 324 | (false, *f) | ||
| 325 | }; | ||
| 326 | let scale: i64 = (d.ln() / *FLOAT_DIVISOR) as i64; | ||
| 327 | let scale = if scale < 0 { -1 } else { scale }; | ||
| 328 | let mut d = d / 256.0_f64.powf(scale as f64); | ||
| 329 | bin.push(d as u8); | ||
| 330 | d = d.fract(); | ||
| 331 | for _ in 0..15 { | ||
| 332 | d *= 256.0; | ||
| 333 | bin.push(d as u8); | ||
| 334 | d = d.fract(); | ||
| 335 | if d == 0.0 { | ||
| 336 | break; | ||
| 337 | } | ||
| 338 | } | ||
| 339 | if negative { | ||
| 340 | w.write_all(&write_packed_int(-(bin.len() as i64)))?; | ||
| 341 | } else { | ||
| 342 | w.write_all(&write_packed_int(bin.len() as i64))?; | ||
| 343 | } | ||
| 344 | w.write_all(&bin)?; | ||
| 345 | w.write_all(&write_packed_int(scale))?; | ||
| 346 | } | ||
| 347 | } | ||
| 348 | } | ||
| 349 | Self::Null => { | ||
| 350 | let bb = [b'n']; | ||
| 351 | w.write_all(&bb)?; | ||
| 352 | } | ||
| 353 | } | ||
| 354 | Ok(()) | ||
| 355 | } | ||
| 356 | |||
| 357 | fn read_parse<R: Read>(r: &mut R) -> Result<ValueParse, Error> { | ||
| 358 | let it = { | ||
| 359 | let mut it = [0u8]; | ||
| 360 | r.read_exact(&mut it)?; | ||
| 361 | it[0] | ||
| 362 | }; | ||
| 363 | match it as char { | ||
| 364 | '0' => Ok(ValueParse::Value(Value::Boolean(false))), | ||
| 365 | '1' => Ok(ValueParse::Value(Value::Boolean(true))), | ||
| 366 | 'n' => Ok(ValueParse::Value(Value::Null)), | ||
| 367 | 'e' => Ok(ValueParse::EndMarker), | ||
| 368 | 'i' => Ok(ValueParse::Value(Value::Integer(read_packed_int(r)?))), | ||
| 369 | 's' => { | ||
| 370 | let len = read_packed_int(r)?; | ||
| 371 | let mut body = vec![0; len as usize]; | ||
| 372 | r.read_exact(&mut body)?; | ||
| 373 | Ok(ValueParse::Value(Value::ByteString(body))) | ||
| 374 | } | ||
| 375 | 'l' => { | ||
| 376 | let mut body = Vec::<Value>::new(); | ||
| 377 | while let ValueParse::Value(v) = Value::read_parse(r)? { | ||
| 378 | body.push(v); | ||
| 379 | } | ||
| 380 | Ok(ValueParse::Value(Value::List(body))) | ||
| 381 | } | ||
| 382 | 'f' => { | ||
| 383 | let (lng, neg) = { | ||
| 384 | let lng = read_packed_int(r)?; | ||
| 385 | if lng < 0 { (-lng, true) } else { (lng, false) } | ||
| 386 | }; | ||
| 387 | let mut value = 0.0f64; | ||
| 388 | let mut buf = vec![0u8; lng as usize]; | ||
| 389 | r.read_exact(&mut buf)?; | ||
| 390 | for b in buf[1..].iter().rev() { | ||
| 391 | value = (value + (*b as f64)) * (1.0 / 256.0); | ||
| 392 | } | ||
| 393 | value += buf[0] as f64; | ||
| 394 | let scale = read_packed_int(r)?; | ||
| 395 | value *= 256.0f64.powi(scale as i32); | ||
| 396 | if neg { | ||
| 397 | Ok(ValueParse::Value(Value::Float(-value))) | ||
| 398 | } else { | ||
| 399 | Ok(ValueParse::Value(Value::Float(value))) | ||
| 400 | } | ||
| 401 | } | ||
| 402 | 'F' => { | ||
| 403 | let st = { | ||
| 404 | let mut st = [0u8]; | ||
| 405 | r.read_exact(&mut st)?; | ||
| 406 | st[0] | ||
| 407 | }; | ||
| 408 | match st as char { | ||
| 409 | 'N' => Ok(ValueParse::Value(Value::Float(f64::NAN))), | ||
| 410 | 'n' => Ok(ValueParse::Value(Value::Float(f64::NAN))), | ||
| 411 | 'I' => Ok(ValueParse::Value(Value::Float(f64::NEG_INFINITY))), | ||
| 412 | 'i' => Ok(ValueParse::Value(Value::Float(f64::INFINITY))), | ||
| 413 | 'Z' => Ok(ValueParse::Value(Value::Float(-0.0f64))), | ||
| 414 | 'z' => Ok(ValueParse::Value(Value::Float(0.0f64))), | ||
| 415 | _ => Err(Error::new( | ||
| 416 | ErrorKind::InvalidData, | ||
| 417 | "Unknown exceptional float subtype found.", | ||
| 418 | )), | ||
| 419 | } | ||
| 420 | } | ||
| 421 | 'd' => { | ||
| 422 | let mut body = HashMap::new(); | ||
| 423 | while let ValueParse::Value(k) = Value::read_parse(r)? { | ||
| 424 | let k = if let Value::ByteString(s) = k { | ||
| 425 | if let Ok(s) = String::from_utf8(s) { | ||
| 426 | s | ||
| 427 | } else { | ||
| 428 | return Err(Error::new( | ||
| 429 | ErrorKind::InvalidData, | ||
| 430 | "Keys must be utf8 compatible strings.", | ||
| 431 | )); | ||
| 432 | } | ||
| 433 | } else { | ||
| 434 | return Err(Error::new( | ||
| 435 | ErrorKind::InvalidData, | ||
| 436 | "Non-string cannot be dictionary key.", | ||
| 437 | )); | ||
| 438 | }; | ||
| 439 | let v = match Value::read_parse(r)? { | ||
| 440 | ValueParse::Value(v) => v, | ||
| 441 | ValueParse::EndMarker => { | ||
| 442 | return Err(Error::new( | ||
| 443 | ErrorKind::InvalidData, | ||
| 444 | "Premature end marker in dictionary.", | ||
| 445 | )); | ||
| 446 | } | ||
| 447 | }; | ||
| 448 | body.insert(k, v); | ||
| 449 | } | ||
| 450 | Ok(ValueParse::Value(Value::Dictionary(body))) | ||
| 451 | } | ||
| 452 | _ => Err(Error::new( | ||
| 453 | ErrorKind::InvalidData, | ||
| 454 | "Invalid data type specified.", | ||
| 455 | )), | ||
| 456 | } | ||
| 457 | } | ||
| 458 | |||
| 459 | pub fn read<R: Read>(r: &mut R) -> Result<Value, Error> { | ||
| 460 | match Value::read_parse(r) { | ||
| 461 | Ok(ValueParse::Value(v)) => Ok(v), | ||
| 462 | Ok(ValueParse::EndMarker) => Err(Error::new( | ||
| 463 | ErrorKind::InvalidData, | ||
| 464 | "Unexpected EndMarker while parsing structure.", | ||
| 465 | )), | ||
| 466 | Err(e) => Err(e), | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | pub fn to_packet(&self) -> Result<Vec<u8>, Error> { | ||
| 471 | let mut body: Vec<u8> = vec![1u8, 0u8, 0u8, 0u8, 0u8]; | ||
| 472 | self.write(&mut body)?; | ||
| 473 | let len = body.len() as u32; | ||
| 474 | body[1..5].copy_from_slice(&len.to_be_bytes()); | ||
| 475 | Ok(body) | ||
| 476 | } | ||
| 477 | |||
| 478 | pub fn write_packet<W: Write>(&self, w: &mut W) -> Result<(), Error> { | ||
| 479 | match self.to_packet() { | ||
| 480 | Ok(body) => w.write_all(&body), | ||
| 481 | Err(e) => Err(e), | ||
| 482 | } | ||
| 483 | } | ||
| 484 | |||
| 485 | #[cfg(feature = "tokio")] | ||
| 486 | pub async fn write_packet_async<W>(&self, w: &mut W) -> Result<(), Error> | ||
| 487 | where | ||
| 488 | W: AsyncWrite + Unpin | ||
| 489 | { | ||
| 490 | match self.to_packet() { | ||
| 491 | Ok(body) => w.write_all(&body).await, | ||
| 492 | Err(e) => Err(e), | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | pub fn bytes_to_read(r: &[u8]) -> Result<BytesToRead, Error> { | ||
| 497 | if r.len() < 5 { | ||
| 498 | Ok(BytesToRead::Estimate(5)) | ||
| 499 | } else { | ||
| 500 | if r[0] != 1 { | ||
| 501 | return Err(Error::new( | ||
| 502 | ErrorKind::InvalidData, | ||
| 503 | "Invalid Gats packet version.", | ||
| 504 | )); | ||
| 505 | } | ||
| 506 | if let Ok(ar) = r[1..5].try_into() { | ||
| 507 | Ok(BytesToRead::Final(i32::from_be_bytes(ar) as usize)) | ||
| 508 | } else { | ||
| 509 | Err(Error::new( | ||
| 510 | ErrorKind::UnexpectedEof, | ||
| 511 | "Insufficient data presented to decode packet header.", | ||
| 512 | )) | ||
| 513 | } | ||
| 514 | } | ||
| 515 | } | ||
| 516 | |||
| 517 | pub fn from_packet(r: &[u8]) -> Result<Value, Error> { | ||
| 518 | if r.len() < 5 { | ||
| 519 | return Err(Error::new( | ||
| 520 | ErrorKind::UnexpectedEof, | ||
| 521 | "Insufficient data presented to decode packet header.", | ||
| 522 | )); | ||
| 523 | } | ||
| 524 | if r[0] != 1 { | ||
| 525 | return Err(Error::new( | ||
| 526 | ErrorKind::InvalidData, | ||
| 527 | "Invalid Gats packet version.", | ||
| 528 | )); | ||
| 529 | } | ||
| 530 | let size = if let Ok(ar) = r[1..5].try_into() { | ||
| 531 | i32::from_be_bytes(ar) | ||
| 532 | } else { | ||
| 533 | return Err(Error::new( | ||
| 534 | ErrorKind::UnexpectedEof, | ||
| 535 | "Insufficient data presented to decode packet header.", | ||
| 536 | )); | ||
| 537 | }; | ||
| 538 | if r.len() != size as usize { | ||
| 539 | return Err(Error::new( | ||
| 540 | ErrorKind::InvalidData, | ||
| 541 | "Packet buffer is the wrong length.", | ||
| 542 | )); | ||
| 543 | } | ||
| 544 | Value::read(&mut Cursor::new(&r[5..])) | ||
| 545 | } | ||
| 546 | |||
| 547 | pub fn read_packet<R: Read>(r: &mut R) -> Result<Value, Error> { | ||
| 548 | let mut buf = Vec::<u8>::new(); | ||
| 549 | let mut fill = 0usize; | ||
| 550 | loop { | ||
| 551 | match Value::bytes_to_read(&buf) { | ||
| 552 | Ok(BytesToRead::Estimate(size)) => { | ||
| 553 | buf.resize(size, 0u8); | ||
| 554 | r.read_exact(&mut buf[fill..])?; | ||
| 555 | fill = buf.len(); | ||
| 556 | } | ||
| 557 | Ok(BytesToRead::Final(size)) => { | ||
| 558 | buf.resize(size, 0u8); | ||
| 559 | r.read_exact(&mut buf[fill..])?; | ||
| 560 | return Value::from_packet(&buf); | ||
| 561 | } | ||
| 562 | Err(e) => { | ||
| 563 | return Err(e); | ||
| 564 | } | ||
| 565 | } | ||
| 566 | } | ||
| 567 | } | ||
| 568 | |||
| 569 | #[cfg(feature = "tokio")] | ||
| 570 | pub async fn read_packet_async<R>(r: &mut R) -> Result<Value, Error> | ||
| 571 | where | ||
| 572 | R: AsyncRead + Unpin | ||
| 573 | { | ||
| 574 | let mut buf = Vec::<u8>::new(); | ||
| 575 | let mut fill = 0usize; | ||
| 576 | loop { | ||
| 577 | match Value::bytes_to_read(&buf) { | ||
| 578 | Ok(BytesToRead::Estimate(size)) => { | ||
| 579 | buf.resize(size, 0u8); | ||
| 580 | let amount = r.read(&mut buf[fill..]).await?; | ||
| 581 | fill += amount; | ||
| 582 | } | ||
| 583 | Ok(BytesToRead::Final(size)) => { | ||
| 584 | buf.resize(size, 0u8); | ||
| 585 | let amount = r.read(&mut buf[fill..]).await?; | ||
| 586 | fill += amount; | ||
| 587 | if fill == size { | ||
| 588 | return Value::from_packet(&buf); | ||
| 589 | } | ||
| 590 | } | ||
| 591 | Err(e) => { | ||
| 592 | return Err(e); | ||
| 593 | } | ||
| 594 | } | ||
| 595 | } | ||
| 596 | } | ||
| 597 | } | ||
| 598 | |||
| 599 | impl From<i64> for Value { | ||
| 600 | fn from(v: i64) -> Self { | ||
| 601 | Value::Integer(v) | ||
| 602 | } | ||
| 603 | } | ||
| 604 | |||
| 605 | impl From<f64> for Value { | ||
| 606 | fn from(v: f64) -> Self { | ||
| 607 | Value::Float(v) | ||
| 608 | } | ||
| 609 | } | ||
| 610 | |||
| 611 | impl From<bool> for Value { | ||
| 612 | fn from(v: bool) -> Self { | ||
| 613 | Value::Boolean(v) | ||
| 614 | } | ||
| 615 | } | ||
| 616 | |||
| 617 | impl From<Vec<u8>> for Value { | ||
| 618 | fn from(v: Vec<u8>) -> Self { | ||
| 619 | Value::ByteString( v ) | ||
| 620 | } | ||
| 621 | } | ||
| 622 | |||
| 623 | impl From<&[u8]> for Value { | ||
| 624 | fn from(v: &[u8]) -> Self { | ||
| 625 | Value::ByteString( Vec::from(v) ) | ||
| 626 | } | ||
| 627 | } | ||
| 628 | |||
| 629 | #[cfg(test)] | ||
| 630 | mod tests { | ||
| 631 | use super::*; | ||
| 632 | use core::error::Error; | ||
| 633 | use std::io::Cursor; | ||
| 634 | |||
| 635 | #[test] | ||
| 636 | fn froms() { | ||
| 637 | let v = Value::from(55); | ||
| 638 | assert!(v == 55); | ||
| 639 | let v = Value::from(987.654); | ||
| 640 | assert!(v == 987.654); | ||
| 641 | let v = Value::from(true); | ||
| 642 | assert!(v == true); | ||
| 643 | let v = Value::from(b"hello".to_vec()); | ||
| 644 | assert!(v == b"hello".to_vec()); | ||
| 645 | } | ||
| 646 | |||
| 647 | #[test] | ||
| 648 | fn round_int() -> Result<(), Box<dyn Error>> { | ||
| 649 | let v = Value::Integer(555666); | ||
| 650 | let mut buf = Vec::<u8>::new(); | ||
| 651 | v.write(&mut buf)?; | ||
| 652 | let v2 = Value::read(&mut Cursor::new(buf))?; | ||
| 653 | assert!(v == v2); | ||
| 654 | assert!(v == 555666); | ||
| 655 | Ok(()) | ||
| 656 | } | ||
| 657 | |||
| 658 | #[test] | ||
| 659 | fn round_str() -> Result<(), Box<dyn Error>> { | ||
| 660 | let v = Value::ByteString(vec![1, 1, 2, 3, 5, 8, 13, 21, 34]); | ||
| 661 | let mut buf = Vec::<u8>::new(); | ||
| 662 | v.write(&mut buf)?; | ||
| 663 | let v2 = Value::read(&mut Cursor::new(buf))?; | ||
| 664 | let v3 = Value::ByteString(vec![1, 1, 2, 3, 5, 8, 13, 21, 35]); | ||
| 665 | assert!(v == v2); | ||
| 666 | assert!(v != v3); | ||
| 667 | assert!(v == vec![1, 1, 2, 3, 5, 8, 13, 21, 34]); | ||
| 668 | Ok(()) | ||
| 669 | } | ||
| 670 | |||
| 671 | #[test] | ||
| 672 | fn round_bool_true() -> Result<(), Box<dyn Error>> { | ||
| 673 | let v = Value::Boolean(true); | ||
| 674 | let mut buf = Vec::<u8>::new(); | ||
| 675 | v.write(&mut buf)?; | ||
| 676 | let v2 = Value::read(&mut Cursor::new(buf))?; | ||
| 677 | let v3 = Value::Boolean(false); | ||
| 678 | assert!(v == v2); | ||
| 679 | assert!(v != v3); | ||
| 680 | assert!(v == true); | ||
| 681 | Ok(()) | ||
| 682 | } | ||
| 683 | |||
| 684 | #[test] | ||
| 685 | fn round_bool_false() -> Result<(), Box<dyn Error>> { | ||
| 686 | let v = Value::Boolean(false); | ||
| 687 | let mut buf = Vec::<u8>::new(); | ||
| 688 | v.write(&mut buf)?; | ||
| 689 | let v2 = Value::read(&mut Cursor::new(buf))?; | ||
| 690 | let v3 = Value::Boolean(true); | ||
| 691 | assert!(v == v2); | ||
| 692 | assert!(v != v3); | ||
| 693 | assert!(v == false); | ||
| 694 | Ok(()) | ||
| 695 | } | ||
| 696 | |||
| 697 | #[test] | ||
| 698 | fn round_list() -> Result<(), Box<dyn Error>> { | ||
| 699 | let v = Value::List(vec![ | ||
| 700 | Value::Integer(1), | ||
| 701 | Value::Integer(1), | ||
| 702 | Value::Integer(2), | ||
| 703 | Value::Integer(3), | ||
| 704 | Value::Integer(5), | ||
| 705 | Value::Integer(8), | ||
| 706 | Value::Integer(13), | ||
| 707 | Value::Integer(21), | ||
| 708 | Value::Integer(34), | ||
| 709 | ]); | ||
| 710 | let mut buf = Vec::<u8>::new(); | ||
| 711 | v.write(&mut buf)?; | ||
| 712 | let v2 = Value::read(&mut Cursor::new(buf))?; | ||
| 713 | assert!(v == v2); | ||
| 714 | Ok(()) | ||
| 715 | } | ||
| 716 | |||
| 717 | #[test] | ||
| 718 | fn round_dictionary() -> Result<(), Box<dyn Error>> { | ||
| 719 | let v = Value::Dictionary(HashMap::from([ | ||
| 720 | ("bigger-int".to_string(), Value::Integer(98765)), | ||
| 721 | ("neg-int".to_string(), Value::Integer(-98765)), | ||
| 722 | ("integer".to_string(), Value::Integer(44)), | ||
| 723 | ("boolean".to_string(), Value::Boolean(true)), | ||
| 724 | ( | ||
| 725 | "list".to_string(), | ||
| 726 | Value::List(vec![ | ||
| 727 | Value::Integer(1), | ||
| 728 | Value::Integer(1), | ||
| 729 | Value::Integer(2), | ||
| 730 | Value::Integer(3), | ||
| 731 | Value::Integer(5), | ||
| 732 | Value::Integer(8), | ||
| 733 | ]), | ||
| 734 | ), | ||
| 735 | ("null".to_string(), Value::Null), | ||
| 736 | ("float".to_string(), Value::Float(123.456)), | ||
| 737 | ])); | ||
| 738 | let mut buf = Vec::<u8>::new(); | ||
| 739 | v.write(&mut buf)?; | ||
| 740 | let v2 = Value::read(&mut Cursor::new(buf))?; | ||
| 741 | assert!(v == v2); | ||
| 742 | Ok(()) | ||
| 743 | } | ||
| 744 | |||
| 745 | #[test] | ||
| 746 | fn round_null() -> Result<(), Box<dyn Error>> { | ||
| 747 | let v = Value::Null; | ||
| 748 | let mut buf = Vec::<u8>::new(); | ||
| 749 | v.write(&mut buf)?; | ||
| 750 | let v2 = Value::read(&mut Cursor::new(buf))?; | ||
| 751 | assert!(v == v2); | ||
| 752 | Ok(()) | ||
| 753 | } | ||
| 754 | |||
| 755 | #[test] | ||
| 756 | fn round_float() -> Result<(), Box<dyn Error>> { | ||
| 757 | let v = Value::Float(123.456); | ||
| 758 | let mut buf = Vec::<u8>::new(); | ||
| 759 | v.write(&mut buf)?; | ||
| 760 | let v2 = Value::read(&mut Cursor::new(buf))?; | ||
| 761 | assert!(v == v2); | ||
| 762 | Ok(()) | ||
| 763 | } | ||
| 764 | |||
| 765 | #[test] | ||
| 766 | fn packet_1() -> Result<(), Box<dyn Error>> { | ||
| 767 | let v = Value::Dictionary(HashMap::from([ | ||
| 768 | ("bigger-int".to_string(), Value::Integer(98765)), | ||
| 769 | ("neg-int".to_string(), Value::Integer(-98765)), | ||
| 770 | ("integer".to_string(), Value::Integer(44)), | ||
| 771 | ("boolean".to_string(), Value::Boolean(true)), | ||
| 772 | ( | ||
| 773 | "list".to_string(), | ||
| 774 | Value::List(vec![ | ||
| 775 | Value::Integer(1), | ||
| 776 | Value::Integer(1), | ||
| 777 | Value::Integer(2), | ||
| 778 | Value::Integer(3), | ||
| 779 | Value::Integer(5), | ||
| 780 | Value::Integer(8), | ||
| 781 | ]), | ||
| 782 | ), | ||
| 783 | ("null".to_string(), Value::Null), | ||
| 784 | ("float".to_string(), Value::Float(123.456)), | ||
| 785 | ])); | ||
| 786 | let buf = v.to_packet()?; | ||
| 787 | let v2 = Value::from_packet(&buf)?; | ||
| 788 | assert!(v == v2); | ||
| 789 | Ok(()) | ||
| 790 | } | ||
| 791 | |||
| 792 | #[cfg(feature = "tokio")] | ||
| 793 | #[test] | ||
| 794 | fn async_test() -> Result<(), Box<dyn Error>> { | ||
| 795 | Ok(()) | ||
| 796 | } | ||
| 797 | } | ||
