Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions src/flatty.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Convert any Nim objects, numbers, strings, refs to and from binary format.
import
std/[tables, typetraits, sets],
std/[importutils, tables, typetraits, sets],
flatty/objvar

when defined(js):
Expand Down Expand Up @@ -68,6 +68,8 @@ func copyable[T](_: typedesc[T]): bool =
false
elif skipsFlattyCopyMem(T):
false
elif T is range:
false
elif (T is pointer) or (T is ptr) or (T is cstring) or (T is proc):
false
elif T is distinct:
Expand Down Expand Up @@ -156,6 +158,7 @@ proc toFlatty*(s: var string, x: float32)
proc toFlatty*(s: var string, x: float64)
proc toFlatty*(s: var string, x: int)
proc toFlatty*(s: var string, x: uint)
proc toFlatty*[T: range](s: var string, x: T)
proc toFlatty*[T: enum and not range](s: var string, x: T)
proc toFlatty*(s: var string, x: string)

Expand All @@ -182,6 +185,7 @@ proc fromFlatty*(s: string, i: var int, x: var int)
proc fromFlatty*(s: string, i: var int, x: var uint)
proc fromFlatty*(s: string, i: var int, x: var float32)
proc fromFlatty*(s: string, i: var int, x: var float64)
proc fromFlatty*[T: range](s: string, i: var int, x: var T)
proc fromFlatty*[T: enum and not range](s: string, i: var int, x: var T)
proc fromFlatty*(s: string, i: var int, x: var string)

Expand Down Expand Up @@ -279,6 +283,17 @@ proc fromFlatty*(s: string, i: var int, x: var float64) =
x = s.readFloat64(i)
i += 8

# Ranges
proc toFlatty*[T: range](s: var string, x: T) =
s.toFlatty(x.rangeBase)

proc fromFlatty*[T: range](s: string, i: var int, x: var T) =
var value: rangeBase(T)
s.fromFlatty(i, value)
if value < low(T).rangeBase or value > high(T).rangeBase:
raise newException(RangeDefect, "value out of range")
x = T(value)

# Enums
proc toFlatty*[T: enum and not range](s: var string, x: T) =
s.addInt64(x.int)
Expand Down Expand Up @@ -334,6 +349,7 @@ proc fromFlatty*[T](s: string, i: var int, x: var seq[T]) =

# Objects
proc toFlatty*[T: object](s: var string, x: T) =
privateAccess(T)
when x.isObjectVariant:
s.toFlatty(x.discriminatorField)
for k, e in x.fieldPairs:
Expand All @@ -344,6 +360,7 @@ proc toFlatty*[T: object](s: var string, x: T) =
s.toFlatty(e)

proc fromFlatty*[T: object](s: string, i: var int, x: var T) =
privateAccess(T)
when x.isObjectVariant:
var discriminator: type(x.discriminatorField)
s.fromFlatty(i, discriminator)
Expand Down Expand Up @@ -412,15 +429,15 @@ proc toFlatty*[N, T](s: var string, x: array[N, T]) =
let byteLen = x.len * sizeof(T)
let oldLen = s.len
s.setLen(oldLen + byteLen)
copyMem(s[oldLen].addr, x[0.N].unsafeAddr, byteLen)
copyMem(s[oldLen].addr, x[low(x)].unsafeAddr, byteLen)
else:
for e in x:
s.toFlatty(e)

proc fromFlatty*[N, T](s: string, i: var int, x: var array[N, T]) =
when not defined(js) and T.copyable:
if x.len > 0:
copyMem(x[0.N].addr, s[i].unsafeAddr, sizeof(x))
copyMem(x[low(x)].addr, s[i].unsafeAddr, sizeof(x))
i += sizeof(x)
else:
for j in x.mitems:
Expand Down
49 changes: 49 additions & 0 deletions tests/test_issue_objects.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import
flatty,
test_issue_objects_types

# #41: imported object variants can keep their discriminator private.
block:
let original = newIssue41Obj(issue41B, "hidden variant payload")
let roundTrip = original.toFlatty.fromFlatty(Issue41Obj)
doAssert roundTrip.kind == issue41B
doAssert roundTrip.value == original.value

# #34: imported objects can have private fields with public getter procs.
block:
let original = initIssue34Bloom(1024'u64, @[1'u64, 8, 27, 64])
let roundTrip = original.toFlatty.fromFlatty(Issue34Bloom)
doAssert roundTrip.capacity == original.capacity
doAssert roundTrip.data == original.data

# #32: arrays with non-zero index ranges use their real lower bound.
block:
var arr: array[-2..0, int]
var arr2: array[10..11, int]

for i in arr.low .. arr.high:
arr[i] = i

for i in arr2.low .. arr2.high:
arr2[i] = i

let arrRoundTrip = arr.toFlatty.fromFlatty(array[-2..0, int])
let arr2RoundTrip = arr2.toFlatty.fromFlatty(array[10..11, int])

for i in arr.low .. arr.high:
doAssert arrRoundTrip[i] == arr[i]

for i in arr2.low .. arr2.high:
doAssert arr2RoundTrip[i] == arr2[i]

# #29: range fields reject out-of-range serialized values.
block:
type RangeObj = object
a: 0..3

let original = RangeObj(a: 3)
doAssert original.toFlatty.fromFlatty(RangeObj) == original

let invalid = 10.toFlatty
doAssertRaises(RangeDefect):
discard invalid.fromFlatty(RangeObj)
40 changes: 40 additions & 0 deletions tests/test_issue_objects_types.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
type
Issue41Kind* = enum
issue41A, issue41B, issue41C

Issue41Obj* = object
case kind: Issue41Kind
of issue41A:
a: string
else:
b: string

Issue34Bloom* = object
capacity: uint64
data: seq[uint64]

proc newIssue41Obj*(kind: Issue41Kind, value: string): Issue41Obj =
case kind
of issue41A:
Issue41Obj(kind: kind, a: value)
else:
Issue41Obj(kind: kind, b: value)

proc kind*(x: Issue41Obj): Issue41Kind =
x.kind

proc value*(x: Issue41Obj): string =
case x.kind
of issue41A:
x.a
else:
x.b

proc initIssue34Bloom*(capacity: uint64, data: seq[uint64]): Issue34Bloom =
Issue34Bloom(capacity: capacity, data: data)

proc capacity*(x: Issue34Bloom): uint64 =
x.capacity

proc data*(x: Issue34Bloom): seq[uint64] =
x.data
2 changes: 1 addition & 1 deletion tests/tests.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import test_binny, test_datetime, test_flatty, test_hashy, test_hexprint,
test_memoryused, test_modes, test_objvar
test_issue_objects, test_memoryused, test_modes, test_objvar

echo "all tests pass"
Loading