aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--c++-libbu++/src/gatsc/main.cpp20
-rw-r--r--c++-libbu++/src/protocolgats.cpp1
-rw-r--r--c++-qt/.gitignore2
-rw-r--r--rust/.gitignore1
-rw-r--r--rust/Cargo.lock32
-rw-r--r--rust/Cargo.toml10
-rw-r--r--rust/src/lib.rs797
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 )
59void Gats::ProtocolGats::writeObject( Gats::Object *pObj ) 59void 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.*
3release 3release
4debug 4debug
5tmp 5tmp
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.
3version = 4
4
5[[package]]
6name = "bytes"
7version = "1.11.0"
8source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
10
11[[package]]
12name = "gats"
13version = "0.1.0"
14dependencies = [
15 "tokio",
16]
17
18[[package]]
19name = "pin-project-lite"
20version = "0.2.16"
21source = "registry+https://github.com/rust-lang/crates.io-index"
22checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
23
24[[package]]
25name = "tokio"
26version = "1.48.0"
27source = "registry+https://github.com/rust-lang/crates.io-index"
28checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
29dependencies = [
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]
2name = "gats"
3version = "0.1.0"
4edition = "2024"
5
6[dependencies]
7tokio = { version = "1.48.0", optional = true, features = ["io-util"] }
8
9[features]
10tokio = ["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
25use std::collections::HashMap;
26use std::io::{Cursor, Error, ErrorKind, Read, Write};
27use std::num::FpCategory;
28use std::sync::LazyLock;
29use std::vec::Vec;
30
31#[cfg(feature = "tokio")]
32use tokio::io::{AsyncReadExt,AsyncWriteExt,AsyncRead,AsyncWrite};
33
34/// Represents a value in a Gats structure.
35#[derive(PartialEq,Debug)]
36pub 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.
51pub 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.
58enum ValueParse {
59 Value(Value),
60 EndMarker,
61}
62
63impl 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
73impl 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
83impl 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
93impl 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
103impl 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
113impl 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
123impl 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
133impl 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.
137static FLOAT_DIVISOR: LazyLock<f64> = LazyLock::new(|| (256_f64).ln());
138
139/// Internal helper to convert a number into a packed format.
140fn 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.
163fn 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
179impl 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
599impl From<i64> for Value {
600 fn from(v: i64) -> Self {
601 Value::Integer(v)
602 }
603}
604
605impl From<f64> for Value {
606 fn from(v: f64) -> Self {
607 Value::Float(v)
608 }
609}
610
611impl From<bool> for Value {
612 fn from(v: bool) -> Self {
613 Value::Boolean(v)
614 }
615}
616
617impl From<Vec<u8>> for Value {
618 fn from(v: Vec<u8>) -> Self {
619 Value::ByteString( v )
620 }
621}
622
623impl From<&[u8]> for Value {
624 fn from(v: &[u8]) -> Self {
625 Value::ByteString( Vec::from(v) )
626 }
627}
628
629#[cfg(test)]
630mod 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}