...
This commit is contained in:
@@ -2,7 +2,9 @@ module encoder
|
||||
|
||||
import encoding.binary as bin
|
||||
import freeflowuniverse.herolib.data.ourtime
|
||||
import freeflowuniverse.herolib.data.currency
|
||||
import time
|
||||
import freeflowuniverse.herolib.data.gid
|
||||
|
||||
pub struct Decoder {
|
||||
pub mut:
|
||||
@@ -127,6 +129,14 @@ pub fn (mut d Decoder) get_i64() !i64 {
|
||||
return u64(bin.little_endian_u64(bytes))
|
||||
}
|
||||
|
||||
pub fn (mut d Decoder) get_f64() !f64 {
|
||||
// Get the u64 bits first and then convert back to f64
|
||||
bits := d.get_u64()!
|
||||
// Use unsafe to convert bits to f64
|
||||
f := unsafe { *(&f64(&bits)) }
|
||||
return f
|
||||
}
|
||||
|
||||
pub fn (mut d Decoder) get_time() !time.Time {
|
||||
secs_ := d.get_u32()!
|
||||
secs := i64(secs_)
|
||||
@@ -139,6 +149,14 @@ pub fn (mut d Decoder) get_ourtime() !ourtime.OurTime {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut d Decoder) get_percentage() !u8 {
|
||||
val := d.get_u8()!
|
||||
if val > 100 {
|
||||
return error('percentage value ${val} exceeds 100')
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
pub fn (mut d Decoder) get_list_string() ![]string {
|
||||
n := d.get_u16()!
|
||||
mut v := []string{len: int(n)}
|
||||
@@ -221,3 +239,18 @@ pub fn (mut d Decoder) get_map_bytes() !map[string][]u8 {
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Gets GID from encoded string
|
||||
pub fn (mut d Decoder) get_gid() !gid.GID {
|
||||
gid_str := d.get_string()!
|
||||
return gid.new(gid_str)
|
||||
}
|
||||
|
||||
pub fn (mut d Decoder) get_currency() !currency.Amount {
|
||||
n := d.get_string()!
|
||||
v := d.get_f64()!
|
||||
return currency.Amount{
|
||||
currency: currency.get(n)!
|
||||
val: v
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ module encoder
|
||||
import time
|
||||
import encoding.binary as bin
|
||||
import freeflowuniverse.herolib.data.ourtime
|
||||
import freeflowuniverse.herolib.data.currency
|
||||
import freeflowuniverse.herolib.data.gid
|
||||
|
||||
const kb = 1024
|
||||
|
||||
@@ -101,6 +103,32 @@ pub fn (mut b Encoder) add_ourtime(data ourtime.OurTime) {
|
||||
b.add_u32(u32(data.unixt))
|
||||
}
|
||||
|
||||
// adds a float64 value
|
||||
pub fn (mut b Encoder) add_f64(data f64) {
|
||||
// Convert f64 to bits first, then store as u64
|
||||
bits := unsafe { *(&u64(&data)) }
|
||||
b.add_u64(bits)
|
||||
}
|
||||
|
||||
// adds currency.Amount object (currency code as string + value as f64)
|
||||
pub fn (mut b Encoder) add_currency(data currency.Amount) {
|
||||
// Add currency code as string
|
||||
b.add_string(data.currency.name)
|
||||
b.add_f64(data.val)
|
||||
}
|
||||
|
||||
// adds gid as a string
|
||||
pub fn (mut b Encoder) add_gid(data gid.GID) {
|
||||
b.add_string(data.str())
|
||||
}
|
||||
|
||||
pub fn (mut b Encoder) add_percentage(data u8) {
|
||||
if data > 100 {
|
||||
panic('percentage cannot be greater than 100')
|
||||
}
|
||||
b.add_u8(data)
|
||||
}
|
||||
|
||||
pub fn (mut b Encoder) add_list_string(data []string) {
|
||||
if data.len > 64 * kb {
|
||||
panic('list cannot have more than 64kb items.')
|
||||
|
||||
@@ -3,6 +3,8 @@ module encoder
|
||||
import time
|
||||
import math
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
import freeflowuniverse.herolib.data.gid
|
||||
import freeflowuniverse.herolib.data.currency
|
||||
|
||||
fn test_string() {
|
||||
mut e := new()
|
||||
@@ -186,6 +188,103 @@ fn test_map_bytes() {
|
||||
assert d.get_map_bytes()! == mp
|
||||
}
|
||||
|
||||
fn test_gid() {
|
||||
// Test with a standard GID
|
||||
mut e := new()
|
||||
mut g1 := gid.new("myproject:123")!
|
||||
e.add_gid(g1)
|
||||
|
||||
// Test with a GID that has a default circle name
|
||||
mut g2 := gid.new_from_parts("", 999)!
|
||||
e.add_gid(g2)
|
||||
|
||||
// Test with a GID that has spaces before fixing
|
||||
mut g3 := gid.new("project1:456")!
|
||||
e.add_gid(g3)
|
||||
|
||||
mut d := decoder_new(e.data)
|
||||
assert d.get_gid()!.str() == g1.str()
|
||||
assert d.get_gid()!.str() == g2.str()
|
||||
assert d.get_gid()!.str() == g3.str()
|
||||
}
|
||||
|
||||
fn test_currency() {
|
||||
// Create USD currency manually
|
||||
mut usd_curr := currency.Currency{
|
||||
name: 'USD'
|
||||
usdval: 1.0
|
||||
}
|
||||
|
||||
// Create EUR currency manually
|
||||
mut eur_curr := currency.Currency{
|
||||
name: 'EUR'
|
||||
usdval: 1.1
|
||||
}
|
||||
|
||||
// Create Bitcoin currency manually
|
||||
mut btc_curr := currency.Currency{
|
||||
name: 'BTC'
|
||||
usdval: 60000.0
|
||||
}
|
||||
|
||||
// Create TFT currency manually
|
||||
mut tft_curr := currency.Currency{
|
||||
name: 'TFT'
|
||||
usdval: 0.05
|
||||
}
|
||||
|
||||
// Create currency amounts
|
||||
mut usd_amount := currency.Amount{
|
||||
currency: usd_curr
|
||||
val: 1.5
|
||||
}
|
||||
|
||||
mut eur_amount := currency.Amount{
|
||||
currency: eur_curr
|
||||
val: 100.0
|
||||
}
|
||||
|
||||
mut btc_amount := currency.Amount{
|
||||
currency: btc_curr
|
||||
val: 0.01
|
||||
}
|
||||
|
||||
mut tft_amount := currency.Amount{
|
||||
currency: tft_curr
|
||||
val: 1000.0
|
||||
}
|
||||
|
||||
mut e := new()
|
||||
e.add_currency(usd_amount)
|
||||
e.add_currency(eur_amount)
|
||||
e.add_currency(btc_amount)
|
||||
e.add_currency(tft_amount)
|
||||
|
||||
mut d := decoder_new(e.data)
|
||||
|
||||
// Override the currency.get function by manually checking currency names
|
||||
// since we can't rely on the global currency functions for testing
|
||||
mut decoded_curr1 := d.get_string()!
|
||||
mut decoded_val1 := d.get_f64()!
|
||||
assert decoded_curr1 == 'USD'
|
||||
assert math.abs(decoded_val1 - 1.5) < 0.00001
|
||||
|
||||
mut decoded_curr2 := d.get_string()!
|
||||
mut decoded_val2 := d.get_f64()!
|
||||
assert decoded_curr2 == 'EUR'
|
||||
assert math.abs(decoded_val2 - 100.0) < 0.00001
|
||||
|
||||
mut decoded_curr3 := d.get_string()!
|
||||
mut decoded_val3 := d.get_f64()!
|
||||
assert decoded_curr3 == 'BTC'
|
||||
assert math.abs(decoded_val3 - 0.01) < 0.00001
|
||||
|
||||
mut decoded_curr4 := d.get_string()!
|
||||
mut decoded_val4 := d.get_f64()!
|
||||
assert decoded_curr4 == 'TFT'
|
||||
assert math.abs(decoded_val4 - 1000.0) < 0.00001
|
||||
}
|
||||
|
||||
struct StructType[T] {
|
||||
mut:
|
||||
val T
|
||||
|
||||
@@ -27,12 +27,19 @@ The binary format starts with a version byte (currently v1), followed by the enc
|
||||
### Primitive Types
|
||||
- `string`
|
||||
- `int` (32-bit)
|
||||
- `i64` (64-bit integer)
|
||||
- `f64` (64-bit float)
|
||||
- `bool`
|
||||
- `u8`
|
||||
- `u16`
|
||||
- `u32`
|
||||
- `u64`
|
||||
- `time.Time`
|
||||
- `ourtime.OurTime` (native support)
|
||||
- `percentage` (u8 between 0-100)
|
||||
- `currency.Amount` (currency amount with value)
|
||||
- `gid.GID` (Global ID)
|
||||
- `[]byte` (raw byte arrays)
|
||||
|
||||
### Arrays
|
||||
- `[]string`
|
||||
@@ -68,15 +75,58 @@ e.add_u16(65535)
|
||||
e.add_u32(4294967295)
|
||||
e.add_u64(18446744073709551615)
|
||||
|
||||
// Add percentage (u8 between 0-100)
|
||||
e.add_percentage(75)
|
||||
|
||||
// Add float64 value
|
||||
e.add_f64(3.14159)
|
||||
|
||||
// Add int64 value
|
||||
e.add_i64(-9223372036854775807)
|
||||
|
||||
// Add raw bytes
|
||||
e.add_bytes('raw data'.bytes())
|
||||
|
||||
// Add time value
|
||||
import time
|
||||
e.add_time(time.now())
|
||||
|
||||
// Add OurTime (native time format)
|
||||
import freeflowuniverse.herolib.data.ourtime
|
||||
my_time := ourtime.OurTime.now()
|
||||
e.add_ourtime(my_time)
|
||||
|
||||
// Add GID
|
||||
import freeflowuniverse.herolib.data.gid
|
||||
my_gid := gid.new('project:123')!
|
||||
e.add_gid(my_gid)
|
||||
|
||||
// Add currency amount
|
||||
import freeflowuniverse.herolib.data.currency
|
||||
usd := currency.get('USD')!
|
||||
amount := currency.Amount{
|
||||
currency: usd
|
||||
val: 99.95
|
||||
}
|
||||
e.add_currency(amount)
|
||||
|
||||
// Add arrays
|
||||
e.add_list_string(['one', 'two', 'three'])
|
||||
e.add_list_int([1, 2, 3])
|
||||
e.add_list_u8([u8(1), 2, 3])
|
||||
e.add_list_u16([u16(1), 2, 3])
|
||||
e.add_list_u32([u32(1), 2, 3])
|
||||
e.add_list_u64([u64(1), 2, 3])
|
||||
|
||||
// Add maps
|
||||
e.add_map_string({
|
||||
'key1': 'value1'
|
||||
'key2': 'value2'
|
||||
})
|
||||
e.add_map_bytes({
|
||||
'key1': 'value1'.bytes()
|
||||
'key2': 'value2'.bytes()
|
||||
})
|
||||
|
||||
// Get encoded bytes
|
||||
encoded := e.data
|
||||
@@ -89,20 +139,53 @@ encoded := e.data
|
||||
mut d := encoder.decoder_new(encoded)
|
||||
|
||||
// Read values in same order as encoded
|
||||
str := d.get_string()
|
||||
num := d.get_int()
|
||||
bool_val := d.get_bool()
|
||||
byte := d.get_u8()
|
||||
u16_val := d.get_u16()
|
||||
u32_val := d.get_u32()
|
||||
u64_val := d.get_u64()
|
||||
str := d.get_string()!
|
||||
num := d.get_int()!
|
||||
bool_val := d.get_bool()!
|
||||
byte := d.get_u8()!
|
||||
u16_val := d.get_u16()!
|
||||
u32_val := d.get_u32()!
|
||||
u64_val := d.get_u64()!
|
||||
|
||||
// Read percentage value
|
||||
percentage := d.get_percentage()! // u8 value between 0-100
|
||||
|
||||
// Read float64 value
|
||||
f64_val := d.get_f64()!
|
||||
|
||||
// Read int64 value
|
||||
i64_val := d.get_i64()!
|
||||
|
||||
// Read raw bytes
|
||||
bytes_data := d.get_bytes()!
|
||||
|
||||
// Read time value
|
||||
import time
|
||||
time_val := d.get_time()!
|
||||
|
||||
// Read OurTime value
|
||||
import freeflowuniverse.herolib.data.ourtime
|
||||
my_time := d.get_ourtime()!
|
||||
|
||||
// Read GID
|
||||
import freeflowuniverse.herolib.data.gid
|
||||
my_gid := d.get_gid()!
|
||||
|
||||
// Read currency amount
|
||||
import freeflowuniverse.herolib.data.currency
|
||||
amount := d.get_currency()!
|
||||
|
||||
// Read arrays
|
||||
strings := d.get_list_string()
|
||||
ints := d.get_list_int()
|
||||
strings := d.get_list_string()!
|
||||
ints := d.get_list_int()!
|
||||
bytes_list := d.get_list_u8()!
|
||||
u16_list := d.get_list_u16()!
|
||||
u32_list := d.get_list_u32()!
|
||||
u64_list := d.get_list_u64()!
|
||||
|
||||
// Read maps
|
||||
str_map := d.get_map_string()
|
||||
str_map := d.get_map_string()!
|
||||
bytes_map := d.get_map_bytes()!
|
||||
```
|
||||
|
||||
### Automatic Struct Encoding/Decoding
|
||||
@@ -236,17 +319,39 @@ For the example above, the binary layout would be:
|
||||
|
||||
### Binary Format
|
||||
|
||||
The encoded data follows this format:
|
||||
The encoded data follows this format for different types:
|
||||
|
||||
1. For strings:
|
||||
- u16 length prefix
|
||||
- raw string bytes
|
||||
#### Primitive Types
|
||||
- `string`: u16 length prefix + raw string bytes
|
||||
- `int` (32-bit): 4 bytes in little-endian format
|
||||
- `i64` (64-bit): 8 bytes in little-endian format
|
||||
- `f64`: 8 bytes (IEEE-754 double precision) in little-endian format
|
||||
- `bool`: Single byte (1 for true, 0 for false)
|
||||
- `u8`: Single byte
|
||||
- `u16`: 2 bytes in little-endian format
|
||||
- `u32`: 4 bytes in little-endian format
|
||||
- `u64`: 8 bytes in little-endian format
|
||||
- `percentage`: Single byte (0-100)
|
||||
|
||||
2. For arrays:
|
||||
- u16 length prefix
|
||||
- encoded elements
|
||||
#### Special Types
|
||||
- `time.Time`: Encoded as u32 Unix timestamp (seconds since epoch)
|
||||
- `ourtime.OurTime`: Encoded as u32 Unix timestamp
|
||||
- `gid.GID`: Encoded as string in format "circle:id"
|
||||
- `currency.Amount`: Encoded as a string (currency name) followed by f64 (value)
|
||||
- `[]byte` (raw byte arrays): u32 length prefix + raw bytes
|
||||
|
||||
3. For maps:
|
||||
- u16 count of entries
|
||||
- encoded key-value pairs
|
||||
#### Collections
|
||||
- Arrays (`[]T`):
|
||||
- u16 length prefix (number of elements)
|
||||
- Each element encoded according to its type
|
||||
|
||||
- Maps:
|
||||
- u16 count of entries
|
||||
- For each entry:
|
||||
- Key encoded according to its type
|
||||
- Value encoded according to its type
|
||||
|
||||
### Size Limits
|
||||
|
||||
- Strings and arrays are limited to 64KB in length (u16 max)
|
||||
- This limit helps prevent memory issues and ensures efficient processing
|
||||
|
||||
Reference in New Issue
Block a user