Skip to content

Commit 8f6a80e

Browse files
committed
add packing feature
1 parent c6bd872 commit 8f6a80e

File tree

2 files changed

+234
-20
lines changed

2 files changed

+234
-20
lines changed

struct.nim

Lines changed: 233 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ type
2222
StructNodeObj = object
2323
case kind*: StructKind
2424
of StructChar:
25-
chr*: char
25+
chr: char
2626
of StructBool:
27-
bval*: bool
27+
bval: bool
2828
of StructShort:
2929
sval: int16
3030
of StructUShort:
@@ -42,7 +42,12 @@ type
4242
of StructDouble:
4343
dval: float64
4444
of StructString:
45-
str*: string
45+
str: string
46+
47+
Struct* = ref StructObj
48+
StructObj = object
49+
fmt: string
50+
vars: seq[StructNode]
4651

4752
StructContext = ref object of RootObj
4853
byteOrder: Endianness
@@ -54,7 +59,7 @@ type
5459

5560

5661
const
57-
VERSION* = "0.1.0"
62+
VERSION* = "0.0.2"
5863

5964
TYPE_LENGTHS = {
6065
'x': sizeof(char),
@@ -73,57 +78,57 @@ const
7378
'?': sizeof(bool)
7479
}.toTable
7580

76-
proc newStructChar(c: char): StructNode =
81+
proc newStructChar*(c: char): StructNode =
7782
new(result)
7883
result.kind = StructChar
7984
result.chr = c
8085

81-
proc newStructBool(b: bool): StructNode =
86+
proc newStructBool*(b: bool): StructNode =
8287
new(result)
8388
result.kind = StructBool
8489
result.bval = b
8590

86-
proc newStructShort(i: int16): StructNode =
91+
proc newStructShort*(i: int16): StructNode =
8792
new(result)
8893
result.kind = StructShort
8994
result.sval = i
9095

91-
proc newStructUShort(i: uint16): StructNode =
96+
proc newStructUShort*(i: uint16): StructNode =
9297
new(result)
9398
result.kind = StructUShort
9499
result.usval = i
95100

96-
proc newStructInt(i: int32): StructNode =
101+
proc newStructInt*(i: int32): StructNode =
97102
new(result)
98103
result.kind = StructInt
99104
result.ival = i
100105

101-
proc newStructUInt(i: uint32): StructNode =
106+
proc newStructUInt*(i: uint32): StructNode =
102107
new(result)
103108
result.kind = StructUInt
104109
result.uival = i
105110

106-
proc newStructQuad(i: int64): StructNode =
111+
proc newStructQuad*(i: int64): StructNode =
107112
new(result)
108113
result.kind = StructQuad
109114
result.qval = i
110115

111-
proc newStructUQuad(i: uint64): StructNode =
116+
proc newStructUQuad*(i: uint64): StructNode =
112117
new(result)
113118
result.kind = StructUQuad
114119
result.uqval = i
115120

116-
proc newStructFloat(f: float32): StructNode =
121+
proc newStructFloat*(f: float32): StructNode =
117122
new(result)
118123
result.kind = StructFloat
119124
result.fval = f
120125

121-
proc newStructDouble(d: float64): StructNode =
126+
proc newStructDouble*(d: float64): StructNode =
122127
new(result)
123128
result.kind = StructDouble
124129
result.dval = d
125130

126-
proc newStructString(s: string): StructNode =
131+
proc newStructString*(s: string): StructNode =
127132
new(result)
128133
result.kind = StructString
129134
result.str = s
@@ -280,7 +285,37 @@ proc load_64f*(s: string, endian: Endianness): float64 {.inline.} =
280285
else:
281286
o[i] = s[8 - i - 1]
282287

283-
proc unpack_byte(vars: var seq[StructNode], ctx: StructContext) =
288+
proc extract_16*[T:int16|uint16](v: T, endian: Endianness): string {.inline.} =
289+
result = ""
290+
var v = v
291+
var o = cast[cstring](addr v)
292+
293+
if endian == littleEndian:
294+
result &= $o[0] & $o[1]
295+
else:
296+
result &= $o[1] & $o[0]
297+
298+
proc extract_32*[T:float32|int32|uint32](v: T, endian: Endianness): string {.inline.} =
299+
result = ""
300+
var v = v
301+
var o = cast[cstring](addr v)
302+
for i in 0..3:
303+
if endian == littleEndian:
304+
result &= $o[i]
305+
else:
306+
result &= $o[3 - i]
307+
308+
proc extract_64*[T:float64|int64|uint64](v: T, endian: Endianness): string {.inline.} =
309+
result = ""
310+
var v = v
311+
var o = cast[cstring](addr v)
312+
for i in 0..7:
313+
if endian == littleEndian:
314+
result &= $o[i]
315+
else:
316+
result &= $o[7 - i]
317+
318+
proc unpack_char(vars: var seq[StructNode], ctx: StructContext) =
284319
for i in 0..ctx.repeat-1:
285320
vars.add(newStructChar(ctx.buffer[ctx.offset]))
286321
ctx.offset += 1
@@ -317,7 +352,6 @@ proc unpack_quad(vars: var seq[StructNode], ctx: StructContext, f: char, signed:
317352
vars.add(newStructUQuad(value.uint64))
318353
ctx.offset += TYPE_LENGTHS[f]
319354

320-
321355
proc unpack_float(vars: var seq[StructNode], ctx: StructContext) =
322356
for i in 0..ctx.repeat-1:
323357
var value = load_32f(ctx.buffer[ctx.offset], ctx.buffer[ctx.offset+1], ctx.buffer[ctx.offset+2], ctx.buffer[ctx.offset+3], ctx.byteOrder)
@@ -341,6 +375,8 @@ proc unpack_string(vars: var seq[StructNode], ctx: StructContext) =
341375
vars.add(newStructString(value))
342376
ctx.offset += ctx.repeat
343377

378+
379+
344380
proc unpack*(fmt, buf: string): seq[StructNode] =
345381
result = @[]
346382

@@ -350,7 +386,6 @@ proc unpack*(fmt, buf: string): seq[StructNode] =
350386

351387
var context = newStructContext()
352388
context.buffer = buf
353-
var fmt = fmt
354389

355390
var repeat = ""
356391
for i in 0..fmt.len-1:
@@ -370,7 +405,7 @@ proc unpack*(fmt, buf: string): seq[StructNode] =
370405
of '=', '<', '>', '!', '@':
371406
context.parse_prefix(f)
372407
of 'b':
373-
unpack_byte(result, context)
408+
unpack_char(result, context)
374409
of '?':
375410
unpack_bool(result, context)
376411
of 'h':
@@ -396,6 +431,172 @@ proc unpack*(fmt, buf: string): seq[StructNode] =
396431
else:
397432
raise newException(ValueError, "bad char in struct format")
398433

434+
proc pack_char(vars: varargs[StructNode], ctx: StructContext): string =
435+
result = ""
436+
for i in 0..ctx.repeat-1:
437+
assert vars[ctx.offset].kind == StructChar
438+
result &= $vars[ctx.offset].chr
439+
ctx.offset += 1
440+
441+
proc pack_bool(vars: varargs[StructNode], ctx: StructContext): string =
442+
result = ""
443+
for i in 0..ctx.repeat-1:
444+
assert vars[ctx.offset].kind == StructBool
445+
if vars[ctx.offset].bval == true:
446+
result &= "\x01"
447+
else:
448+
result &= "\x00"
449+
ctx.offset += 1
450+
451+
proc pack_16(vars: varargs[StructNode], ctx: StructContext): string =
452+
result = ""
453+
for i in 0..ctx.repeat-1:
454+
case vars[ctx.offset].kind:
455+
of StructShort:
456+
result &= extract_16(vars[ctx.offset].sval, ctx.byteOrder)
457+
of StructUShort:
458+
result &= extract_16(vars[ctx.offset].usval, ctx.byteOrder)
459+
else:
460+
raise newException(ValueError, "not supported")
461+
ctx.offset += 1
462+
463+
464+
proc pack_32(vars: varargs[StructNode], ctx: StructContext): string =
465+
result = ""
466+
for i in 0..ctx.repeat-1:
467+
case vars[ctx.offset].kind:
468+
of StructFloat:
469+
result &= extract_32(vars[ctx.offset].fval, ctx.byteOrder)
470+
of StructInt:
471+
result &= extract_32(vars[ctx.offset].ival, ctx.byteOrder)
472+
of StructUInt:
473+
result &= extract_32(vars[ctx.offset].uival, ctx.byteOrder)
474+
else:
475+
raise newException(ValueError, "not supported")
476+
ctx.offset += 1
477+
478+
proc pack_64(vars: varargs[StructNode], ctx: StructContext): string =
479+
result = ""
480+
for i in 0..ctx.repeat-1:
481+
case vars[ctx.offset].kind:
482+
of StructDouble:
483+
result &= extract_64(vars[ctx.offset].dval, ctx.byteOrder)
484+
of StructQuad:
485+
result &= extract_64(vars[ctx.offset].qval, ctx.byteOrder)
486+
of StructUQuad:
487+
result &= extract_64(vars[ctx.offset].uqval, ctx.byteOrder)
488+
else:
489+
raise newException(ValueError, "not supported")
490+
ctx.offset += 1
491+
492+
proc pack_string(vars: varargs[StructNode], ctx: StructContext): string =
493+
result = ""
494+
assert vars[ctx.offset].kind == StructString
495+
result &= vars[ctx.offset].str[0..ctx.repeat-1]
496+
497+
var pad = ctx.repeat - vars[ctx.offset].str.len
498+
if pad > 0:
499+
result &= "\x00".repeat(pad)
500+
501+
ctx.offset += 1
502+
503+
proc pack_pad(ctx: StructContext): string =
504+
result = ""
505+
for i in 0..ctx.repeat-1:
506+
result &= "\x00"
507+
508+
proc pack*(fmt: string, vars: varargs[StructNode]): string =
509+
result = ""
510+
var context = newStructContext()
511+
512+
var repeat = ""
513+
for i in 0..fmt.len-1:
514+
let f: char = fmt[i]
515+
516+
if f in '0'..'9':
517+
repeat.add($f)
518+
continue
519+
else:
520+
if repeat == "":
521+
context.repeat = 1
522+
else:
523+
context.repeat = parseInt(repeat)
524+
repeat = ""
525+
526+
case f
527+
of '=', '<', '>', '!', '@':
528+
context.parse_prefix(f)
529+
of 'b':
530+
result &= pack_char(vars, context)
531+
of '?':
532+
result &= pack_bool(vars, context)
533+
of 'h', 'H':
534+
result &= pack_16(vars, context)
535+
of 'i', 'I', 'f':
536+
result &= pack_32(vars, context)
537+
of 'q', 'Q', 'd':
538+
result &= pack_64(vars, context)
539+
of 's':
540+
result &= pack_string(vars, context)
541+
of 'x':
542+
result &= pack_pad(context)
543+
else:
544+
raise newException(ValueError, "bad char in struct format")
545+
546+
547+
proc newStruct(fmt: string): Struct =
548+
new(result)
549+
result.fmt = fmt
550+
result.vars = @[]
551+
552+
proc add(s: Struct, c: char): Struct =
553+
result = s
554+
s.vars.add(newStructChar(c))
555+
556+
proc add(s: Struct, b: bool): Struct =
557+
result = s
558+
s.vars.add(newStructBool(b))
559+
560+
proc add(s: Struct, i: int16): Struct =
561+
result = s
562+
s.vars.add(newStructShort(i))
563+
564+
proc add(s: Struct, i: uint16): Struct =
565+
result = s
566+
s.vars.add(newStructUShort(i))
567+
568+
proc add(s: Struct, i: int32): Struct =
569+
result = s
570+
s.vars.add(newStructInt(i))
571+
572+
proc add(s: Struct, i: uint32): Struct =
573+
result = s
574+
s.vars.add(newStructUint(i))
575+
576+
proc add(s: Struct, i: int64): Struct =
577+
result = s
578+
s.vars.add(newStructQuad(i))
579+
580+
proc add(s: Struct, i: uint64): Struct =
581+
result = s
582+
s.vars.add(newStructUQuad(i))
583+
584+
proc add(s: Struct, f: float32): Struct =
585+
result = s
586+
s.vars.add(newStructFloat(f))
587+
588+
proc add(s: Struct, d: float64): Struct =
589+
result = s
590+
s.vars.add(newStructDouble(d))
591+
592+
proc add(s: Struct, str: string): Struct =
593+
result = s
594+
s.vars.add(newStructString(str))
595+
596+
proc pack*(s: Struct): string =
597+
result = pack(s.fmt, s.vars)
598+
599+
399600
when isMainModule:
400601
let buf ="\x41\x42\x43\x44\x45\x01\x00\x07\x08\x01\x02\x03\x04\x0D\x00\x00\x00"
401602
let result1 = unpack("<5b2?h2i", buf)
@@ -418,3 +619,16 @@ when isMainModule:
418619
assert result4[0].getString == "Viet"
419620
assert result4[1].getString == "Nam"
420621
echo result4
622+
623+
#echo pack("<fi?c", newStructFloat(5.2), newStructInt(8), newStructBool(true), newStructChar('a'))
624+
var format = "<ffb2?biQdH"
625+
var st = newStruct(format)
626+
discard st.add(5.2'f32).add(6.4'f32).add('A').add(true).add(false)
627+
var out1 = st.add('a').add(8'i32).add(8589934591).add(10.4'f64).add(32767.int16).pack()
628+
echo out1
629+
echo unpack(format, out1)
630+
631+
assert newStruct("h").add(32767.int16).pack() == "\xff\x7f"
632+
633+
assert newStruct("4s3s").add("Viet").add("Nam").pack() == "VietNam"
634+
assert newStruct("6sxxxxx3s").add("Viet").add("Nam").pack().len == 14

struct.nimble

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[Package]
22
name = "struct"
3-
version = "0.1.0"
3+
version = "0.0.2"
44
author = "Huy ☭"
55
description = "Python-like 'struct' for Nim"
66
license = "MIT"

0 commit comments

Comments
 (0)