Skip to content

Threaded IO datastore with minimal modifications #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 173 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
173 commits
Select commit Hold shift + click to select a range
b322d0b
setup the path correctly
dryajov Sep 7, 2023
030dc9e
fix conflicting testing symbols
dryajov Sep 7, 2023
542fdf2
initial data buffer
elcritch Aug 24, 2023
e81c5fa
add helpers
elcritch Aug 24, 2023
da0eafe
setup shared datastore
elcritch Aug 24, 2023
57450fe
make blank sharedds
elcritch Aug 24, 2023
e592aaf
updating items
elcritch Aug 24, 2023
edc9321
updates
elcritch Aug 24, 2023
d39c096
setting up thread backend
elcritch Aug 24, 2023
d09ef9b
plumbing
elcritch Aug 24, 2023
055edb4
plumbing values
elcritch Aug 25, 2023
5ef4196
plumbing values
elcritch Aug 25, 2023
3bde00d
plumbing values
elcritch Aug 25, 2023
a0d60cc
reworking results and tasks
elcritch Aug 25, 2023
972ee22
more changes
elcritch Aug 25, 2023
ec4b442
more changes
elcritch Aug 25, 2023
368c251
more changes
elcritch Aug 25, 2023
2809788
getting somewhere now
elcritch Aug 25, 2023
8644cbc
fixes
elcritch Aug 25, 2023
567e0d7
fixes
elcritch Aug 25, 2023
321a4b6
cleanup
elcritch Aug 25, 2023
567f84e
cleanup
elcritch Aug 25, 2023
b48e45c
adding testing
elcritch Aug 25, 2023
0daf168
adding testing
elcritch Aug 25, 2023
d5fe24c
adding testing
elcritch Aug 25, 2023
2e9695e
adding testing
elcritch Aug 25, 2023
464896b
adding test backend
elcritch Aug 25, 2023
6c5059e
fixing tp setup
elcritch Aug 25, 2023
0790cca
setup sharedptr
elcritch Aug 25, 2023
a9e9076
updates
elcritch Aug 25, 2023
ab850d4
updates
elcritch Aug 25, 2023
a3cff98
updates
elcritch Aug 25, 2023
8ab5da0
create backend
elcritch Aug 25, 2023
3da60e0
add threadid
elcritch Aug 25, 2023
f66531d
passing put args
elcritch Aug 25, 2023
0b2e016
adding sharedds get
elcritch Aug 25, 2023
6c133a2
cleanup
elcritch Aug 25, 2023
86a7085
switching up
elcritch Aug 25, 2023
aa01665
switching up errors
elcritch Aug 25, 2023
35c4466
switching up errors
elcritch Aug 25, 2023
727f8b6
properly close signal even on cancel
elcritch Aug 25, 2023
0d4e6f2
switch to smartptrs
elcritch Aug 25, 2023
768de3b
cleanup
elcritch Aug 25, 2023
89f43f5
more cleanup
elcritch Aug 25, 2023
b90eff2
cleanup
elcritch Aug 29, 2023
a359edb
cleanup
elcritch Aug 29, 2023
d60bd35
rework ds
elcritch Aug 29, 2023
88e26f9
rework ds
elcritch Aug 29, 2023
d70e0df
rework ds
elcritch Aug 29, 2023
904d65b
add memory (test) ds
elcritch Aug 29, 2023
6399534
add memory (test) ds
elcritch Aug 29, 2023
9ee931b
add memory (test) ds
elcritch Aug 29, 2023
de406a5
add memory (test) ds
elcritch Aug 29, 2023
9b29a00
add memory (test) ds
elcritch Aug 29, 2023
170d50d
add memory (test) ds
elcritch Aug 29, 2023
985697b
add memory (test) ds
elcritch Aug 29, 2023
a7f6794
add memory (test) ds
elcritch Aug 29, 2023
23423e5
add memory (test) ds
elcritch Aug 29, 2023
344ba6a
basic datstore checks
elcritch Aug 29, 2023
a11a681
basic datstore checks
elcritch Aug 29, 2023
8445920
passing basic ds tests
elcritch Aug 29, 2023
84986c7
sorting keys - fix
elcritch Aug 29, 2023
18e96b6
cleanup
elcritch Aug 29, 2023
f318877
cleanup
elcritch Aug 29, 2023
b27593e
cleanup
elcritch Aug 29, 2023
51683be
cleanup
elcritch Aug 29, 2023
03830ce
cleanup
elcritch Aug 29, 2023
a68ce94
test setup
elcritch Aug 29, 2023
6364c83
test setup
elcritch Aug 29, 2023
40cc03e
updates
elcritch Aug 29, 2023
22c9d85
rename sharedds to threadproxyds
elcritch Aug 29, 2023
2cded13
impl delete
elcritch Aug 29, 2023
9fa6c6e
impl has
elcritch Aug 29, 2023
db666f9
impl has
elcritch Aug 29, 2023
c21532b
impl has
elcritch Aug 29, 2023
11267c8
first impl of batch ops
elcritch Aug 29, 2023
a2c9a7e
various cleanup
elcritch Aug 29, 2023
1e6fc45
don't test 1.2
elcritch Aug 29, 2023
485f2ec
cleanup
elcritch Aug 29, 2023
9e0b976
change TResult to case type
elcritch Aug 29, 2023
5f13765
switch to result type
elcritch Aug 29, 2023
7a3c64d
switch to result type
elcritch Aug 29, 2023
9d6e6ae
cleanup
elcritch Aug 29, 2023
e52f794
add querybuffer type
elcritch Aug 29, 2023
a2f7745
add querybuffer type
elcritch Aug 29, 2023
3f8c471
add querybuffer type
elcritch Aug 29, 2023
120f770
implementing query type
elcritch Aug 29, 2023
14c3947
implementing query type
elcritch Aug 29, 2023
f1d2a0e
implementing query type
elcritch Aug 29, 2023
428b3e6
implementing query type
elcritch Aug 29, 2023
2979ae0
implementing query type
elcritch Aug 29, 2023
9ea1b58
implementing query type
elcritch Aug 29, 2023
76ec7e7
implementing query type
elcritch Aug 30, 2023
2c881d9
cleanup
elcritch Aug 30, 2023
3f3e4b8
update nim
elcritch Aug 30, 2023
64434b7
stylechecks workaround
elcritch Aug 30, 2023
da83b04
remove stylechecks workaround
elcritch Aug 30, 2023
a95cd9c
bump required nim version
elcritch Aug 30, 2023
b1a5b9c
implementing query type
elcritch Aug 30, 2023
52286b8
query iterator using items is breaks when the DS isn't blocking
elcritch Aug 30, 2023
ed508b4
query iterator using items is breaks when the DS isn't blocking
elcritch Aug 30, 2023
9b004cd
query iterator using items is breaks when the DS isn't blocking
elcritch Aug 30, 2023
8fccc77
query iterator using items is breaks when the DS isn't blocking
elcritch Aug 30, 2023
3a9ee98
query iterator using items is breaks when the DS isn't blocking
elcritch Aug 30, 2023
221d193
query iterator using items is breaks when the DS isn't blocking
elcritch Aug 30, 2023
2d2b663
compiler really doesn't like this
elcritch Aug 30, 2023
1f00125
fix compiler issue -- wasn't detecting discard on result correctly
elcritch Aug 30, 2023
0caa606
fix test
elcritch Aug 31, 2023
619d3e9
add tests for new ds'es
elcritch Aug 31, 2023
24ce85e
add tests for new ds'es
elcritch Aug 31, 2023
f2bfe7a
add tests for new ds'es
elcritch Aug 31, 2023
2a04a36
add tests for new ds'es
elcritch Aug 31, 2023
1760ae2
set manual version of chronos
elcritch Aug 31, 2023
e84ccb2
add taskpools
elcritch Aug 31, 2023
6c3b0d6
fix unitest2 import
dryajov Sep 7, 2023
146cbcb
wip
dryajov Sep 8, 2023
b7454d6
foreign buffer
dryajov Sep 11, 2023
9bbf3ed
use shared table
dryajov Sep 12, 2023
13e89bc
adding semaphore for async backpresure
dryajov Sep 12, 2023
184420c
re-adding databuf
dryajov Sep 12, 2023
5adc7c9
this would never work
dryajov Sep 12, 2023
4c48383
reworked with less copying
dryajov Sep 12, 2023
2829ac8
remove useless async annotation
dryajov Sep 12, 2023
d6c4d97
add $ operator
dryajov Sep 13, 2023
19954c6
quick and dirty query
dryajov Sep 13, 2023
776c58f
re-added query and added raises (do they work?)
dryajov Sep 13, 2023
7a9bc11
make concurrent (but don't need anymore)
dryajov Sep 13, 2023
6a3882f
reverted query back (it works as is)
dryajov Sep 13, 2023
88eb96e
don't use toSeq
dryajov Sep 13, 2023
6c86d2b
make sure all tests pass
dryajov Sep 13, 2023
f003812
remove asyncsemaphore
dryajov Sep 14, 2023
af31030
enable sqllite threading tests
dryajov Sep 14, 2023
3500913
adding semaphore
dryajov Sep 14, 2023
221e93f
remove asyncsemaphore
dryajov Sep 15, 2023
e39bd53
wip
dryajov Sep 15, 2023
812b4fd
use foreach iterator
dryajov Sep 15, 2023
6d5c471
add sqlite query tests
dryajov Sep 15, 2023
a96de2c
fix
dryajov Sep 15, 2023
59f1f0b
add semaphore tests
dryajov Sep 15, 2023
25678b2
change iter constructore back to new
dryajov Sep 15, 2023
a17e9fe
remove memdb (not needed)
dryajov Sep 15, 2023
b2cb1fd
remove memdb tests
dryajov Sep 15, 2023
0fde28a
move assert into lock
dryajov Sep 15, 2023
71c704f
adding todo
dryajov Sep 15, 2023
600dca6
adding serialization to query iter
dryajov Sep 15, 2023
783ecc3
fix databuffer tests
dryajov Sep 15, 2023
7f6d95b
adding databuffer tests
dryajov Sep 15, 2023
20d7234
re-enable missing key check
dryajov Sep 15, 2023
1713c76
adding back async semaphore
dryajov Sep 15, 2023
d151c01
enable cancellations
dryajov Sep 15, 2023
9e27eec
adding chronicles and generating lock file
dryajov Sep 15, 2023
f6acaa6
add async semaphore tests
dryajov Sep 15, 2023
bee79ff
added (ugly!) locking capabilities
dryajov Sep 15, 2023
84681cd
make all tests pass
dryajov Sep 15, 2023
f849c1c
re-export threaded datastore
dryajov Sep 15, 2023
52d6a85
fixing dumb error in trace
dryajov Sep 15, 2023
7306a0b
simplify locking
dryajov Sep 18, 2023
ed09b9c
re-enable tests
dryajov Sep 18, 2023
75fa37f
avoid duplicating code
dryajov Sep 18, 2023
2c5186e
use baseAddr
dryajov Sep 18, 2023
d5a1b34
remove ptr to Datastore in TaskCtx, it's a ref
dryajov Sep 19, 2023
3d33820
change logscope
dryajov Sep 20, 2023
0beb3d3
handle error passing and conversion better
dryajov Sep 20, 2023
bb304a2
fix broken key not found test
dryajov Sep 20, 2023
181168d
fix tired ds
dryajov Sep 20, 2023
14f8c3a
check for nil ctx and set iter.finished correctly
dryajov Sep 20, 2023
7ceccf9
get rid of unsafeAddr everywhere
dryajov Sep 20, 2023
2eb120b
make path threadvar
dryajov Sep 20, 2023
81372c9
duh
dryajov Sep 20, 2023
3d84781
copy data and keys to thread local gc
dryajov Sep 20, 2023
9013a0c
GC_fullcollect() in tests to check mem consistency
dryajov Sep 20, 2023
215d334
avoid segfault on fullcollect
dryajov Sep 20, 2023
4cd74af
add arc/orc support
dryajov Sep 21, 2023
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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
fail-fast: false
matrix:
cache_nonce: [ 1 ]
nim_version: [ 1.2.18, 1.6.6 ]
nim_version: [ 1.6.14 ]
platform:
- {
icon: 🐧,
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ coverage
datastore.nims
nimcache
TODO
nim.cfg
nimble.develop
nimble.paths
4 changes: 4 additions & 0 deletions config.nims
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ when (NimMajor, NimMinor) == (1, 2):

when (NimMajor, NimMinor) > (1, 2):
switch("hint", "XCannotRaiseY:off")
# begin Nimble config (version 2)
when withDir(thisDir(), system.fileExists("nimble.paths")):
include "nimble.paths"
# end Nimble config
3 changes: 2 additions & 1 deletion datastore.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ import ./datastore/fsds
import ./datastore/sql
import ./datastore/mountedds
import ./datastore/tieredds
import ./datastore/threads/threadproxyds

export datastore, fsds, mountedds, tieredds, sql
export datastore, fsds, mountedds, tieredds, sql, threadproxyds
10 changes: 7 additions & 3 deletions datastore.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ author = "Status Research & Development GmbH"
description = "Simple, unified API for multiple data stores"
license = "Apache License 2.0 or MIT"

requires "nim >= 1.2.0",
requires "nim >= 1.6.14",
"asynctest >= 0.3.1 & < 0.4.0",
"chronos",
"chronos#0277b65be2c7a365ac13df002fba6e172be55537",
"questionable >= 0.10.3 & < 0.11.0",
"sqlite3_abi",
"stew",
"unittest2",
"upraises >= 0.1.0 & < 0.2.0"
"pretty",
"threading",
"taskpools",
"upraises >= 0.1.0 & < 0.2.0",
"chronicles"

task coverage, "generates code coverage report":
var (output, exitCode) = gorgeEx("which lcov")
Expand Down
18 changes: 9 additions & 9 deletions datastore/datastore.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,32 @@ push: {.upraises: [].}
type
BatchEntry* = tuple[key: Key, data: seq[byte]]

method has*(self: Datastore, key: Key): Future[?!bool] {.base, locks: "unknown".} =
method has*(self: Datastore, key: Key): Future[?!bool] {.base, locks: "unknown", raises: [].} =
raiseAssert("Not implemented!")

method delete*(self: Datastore, key: Key): Future[?!void] {.base, locks: "unknown".} =
method delete*(self: Datastore, key: Key): Future[?!void] {.base, locks: "unknown", raises: [].} =
raiseAssert("Not implemented!")

method delete*(self: Datastore, keys: seq[Key]): Future[?!void] {.base, locks: "unknown".} =
method delete*(self: Datastore, keys: seq[Key]): Future[?!void] {.base, locks: "unknown", raises: [].} =
raiseAssert("Not implemented!")

method get*(self: Datastore, key: Key): Future[?!seq[byte]] {.base, locks: "unknown".} =
method get*(self: Datastore, key: Key): Future[?!seq[byte]] {.base, locks: "unknown", raises: [].} =
raiseAssert("Not implemented!")

method put*(self: Datastore, key: Key, data: seq[byte]): Future[?!void] {.base, locks: "unknown".} =
method put*(self: Datastore, key: Key, data: seq[byte]): Future[?!void] {.base, locks: "unknown", raises: [].} =
raiseAssert("Not implemented!")

method put*(self: Datastore, batch: seq[BatchEntry]): Future[?!void] {.base, locks: "unknown".} =
method put*(self: Datastore, batch: seq[BatchEntry]): Future[?!void] {.base, locks: "unknown", raises: [].} =
raiseAssert("Not implemented!")

method close*(self: Datastore): Future[?!void] {.base, async, locks: "unknown".} =
method close*(self: Datastore): Future[?!void] {.base, locks: "unknown", raises: [].} =
raiseAssert("Not implemented!")

method query*(
self: Datastore,
query: Query): Future[?!QueryIter] {.base, gcsafe.} =
query: Query): Future[?!QueryIter] {.base, gcsafe, raises: [].} =

raiseAssert("Not implemented!")

proc contains*(self: Datastore, key: Key): Future[bool] {.async.} =
proc contains*(self: Datastore, key: Key): Future[bool] {.async, raises: [].} =
return (await self.has(key)) |? false
16 changes: 16 additions & 0 deletions datastore/fsds.nim
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ method put*(
return success()

proc dirWalker(path: string): iterator: string {.gcsafe.} =
var localPath {.threadvar.}: string

localPath = path
return iterator(): string =
try:
for p in path.walkDirRec(yieldFilter = {pcFile}, relative = true):
Expand Down Expand Up @@ -188,10 +191,23 @@ method query*(
var
iter = QueryIter.new()

var lock = newAsyncLock() # serialize querying under threads
proc next(): Future[?!QueryResponse] {.async.} =
defer:
if lock.locked:
lock.release()

if lock.locked:
return failure (ref DatastoreError)(msg: "Should always await query features")

let
path = walker()

if iter.finished:
return failure "iterator is finished"

await lock.acquire()

if finished(walker):
iter.finished = true
return success (Key.none, EmptyBytes)
Expand Down
15 changes: 7 additions & 8 deletions datastore/query.nim
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import pkg/upraises
import std/algorithm
import pkg/chronos
import pkg/questionable
import pkg/questionable/results

import ./key
import ./types

type
SortOrder* {.pure.} = enum
Assending,
Descending
export types
export options, SortOrder

type
Query* = object
key*: Key # Key to be queried
value*: bool # Flag to indicate if data should be returned
Expand All @@ -19,11 +19,10 @@ type
sort*: SortOrder # Sort order - not available in all backends

QueryResponse* = tuple[key: ?Key, data: seq[byte]]
QueryEndedError* = object of DatastoreError

GetNext* = proc(): Future[?!QueryResponse] {.upraises: [], gcsafe, closure.}
GetNext* = proc(): Future[?!QueryResponse] {.upraises: [], gcsafe.}
IterDispose* = proc(): Future[?!void] {.upraises: [], gcsafe.}
QueryIter* = ref object
QueryIter* {.acyclic.} = ref object
finished*: bool
next*: GetNext
dispose*: IterDispose
Expand All @@ -42,7 +41,7 @@ proc init*(
T: type Query,
key: Key,
value = true,
sort = SortOrder.Assending,
sort = SortOrder.Ascending,
offset = 0,
limit = -1): T =

Expand Down
12 changes: 11 additions & 1 deletion datastore/sql/sqliteds.nim
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,19 @@ method query*(
if not (v == SQLITE_OK):
return failure newException(DatastoreError, $sqlite3_errstr(v))

let lock = newAsyncLock()
proc next(): Future[?!QueryResponse] {.async.} =
defer:
if lock.locked:
lock.release()

if lock.locked:
return failure (ref DatastoreError)(msg: "Should always await query features")

if iter.finished:
return failure(newException(QueryEndedError, "Calling next on a finished query!"))
return failure((ref QueryEndedError)(msg: "Calling next on a finished query!"))

await lock.acquire()

let
v = sqlite3_step(s)
Expand Down
3 changes: 2 additions & 1 deletion datastore/sql/sqliteutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ proc bindParam(
# to the return from sqlite3_bind_*(). The object and pointer to it
# must remain valid until then. SQLite will then manage the lifetime of
# its private copy."
sqlite3_bind_blob(s, n.cint, unsafeAddr val[0], val.len.cint,
var val = val
sqlite3_bind_blob(s, n.cint, addr val[0], val.len.cint,
SQLITE_TRANSIENT)
else:
sqlite3_bind_null(s, n.cint)
Expand Down
96 changes: 96 additions & 0 deletions datastore/threads/asyncsemaphore.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Nim-Libp2p
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.

{.push raises: [].}

import sequtils
import chronos, chronicles

# TODO: this should probably go in chronos

logScope:
topics = "datastore semaphore"

type
AsyncSemaphore* = ref object of RootObj
size*: int
count: int
queue: seq[Future[void]]

func new*(_: type AsyncSemaphore, size: int): AsyncSemaphore =
AsyncSemaphore(size: size, count: size)

proc `count`*(s: AsyncSemaphore): int = s.count

proc tryAcquire*(s: AsyncSemaphore): bool =
## Attempts to acquire a resource, if successful
## returns true, otherwise false
##

if s.count > 0 and s.queue.len == 0:
s.count.dec
trace "Acquired slot", available = s.count, queue = s.queue.len
return true

proc acquire*(s: AsyncSemaphore): Future[void] =
## Acquire a resource and decrement the resource
## counter. If no more resources are available,
## the returned future will not complete until
## the resource count goes above 0.
##

let fut = newFuture[void]("AsyncSemaphore.acquire")
if s.tryAcquire():
fut.complete()
return fut

proc cancellation(udata: pointer) {.gcsafe.} =
fut.cancelCallback = nil
if not fut.finished:
s.queue.keepItIf( it != fut )

fut.cancelCallback = cancellation

s.queue.add(fut)

trace "Queued slot", available = s.count, queue = s.queue.len
return fut

proc forceAcquire*(s: AsyncSemaphore) =
## ForceAcquire will always succeed,
## creating a temporary slot if required.
## This temporary slot will stay usable until
## there is less `acquire`s than `release`s
s.count.dec

proc release*(s: AsyncSemaphore) =
## Release a resource from the semaphore,
## by picking the first future from the queue
## and completing it and incrementing the
## internal resource count
##

doAssert(s.count <= s.size)

if s.count < s.size:
trace "Releasing slot", available = s.count,
queue = s.queue.len

s.count.inc
while s.queue.len > 0:
var fut = s.queue[0]
s.queue.delete(0)
if not fut.finished():
s.count.dec
fut.complete()
break

trace "Released slot", available = s.count,
queue = s.queue.len
return
92 changes: 92 additions & 0 deletions datastore/threads/databuffer.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import threading/smartptrs
import std/hashes
import pkg/stew/ptrops

export hashes

type
DataBufferHolder* = object
buf: ptr UncheckedArray[byte]
size: int

DataBuffer* = SharedPtr[DataBufferHolder] ##\
## A fixed length data buffer using a SharedPtr.
## It is thread safe even with `refc` since
## it doesn't use string or seq types internally.
##

proc `=destroy`*(x: var DataBufferHolder) =
## copy pointer implementation
##

if x.buf != nil:
# echo "buffer: FREE: ", repr x.buf.pointer
deallocShared(x.buf)

proc len*(a: DataBuffer): int = a[].size

proc isNil*(a: DataBuffer): bool = smartptrs.isNil(a)

proc hash*(a: DataBuffer): Hash =
a[].buf.toOpenArray(0, a[].size-1).hash()

proc `==`*(a, b: DataBuffer): bool =
if a.isNil and b.isNil: return true
elif a.isNil or b.isNil: return false
elif a[].size != b[].size: return false
elif a[].buf == b[].buf: return true
else: a.hash() == b.hash()

proc new*(tp: type DataBuffer, size: int = 0): DataBuffer =
## allocate new buffer with given size
##

newSharedPtr(DataBufferHolder(
buf: cast[typeof(result[].buf)](allocShared0(size)),
size: size,
))

proc new*[T: byte | char](tp: type DataBuffer, data: openArray[T]): DataBuffer =
## allocate new buffer and copies indata from openArray
##
result = DataBuffer.new(data.len)
if data.len() > 0:
# TODO: we might want to copy data, otherwise the GC might
# release it on stack-unwind
copyMem(result[].buf, baseAddr data, data.len)

converter toSeq*(self: DataBuffer): seq[byte] =
## convert buffer to a seq type using copy and either a byte or char
##

result = newSeq[byte](self.len)
if self.len() > 0:
copyMem(addr result[0], addr self[].buf[0], self.len)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good! I was thinking about that this weekend.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, but maybe this should also be switched to baseAddr? Not sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or the other way around, switch baseAddr to addr?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo, addr is probably safer, baseAddr does a straight up cast.


proc `@`*(self: DataBuffer): seq[byte] =
## Convert a buffer to a seq type using copy and
## either a byte or char
##

self.toSeq()

converter toString*(data: DataBuffer): string =
## convert buffer to string type using copy
##

if data.isNil: return ""
result = newString(data.len())
if data.len() > 0:
copyMem(addr result[0], addr data[].buf[0], data.len)

proc `$`*(data: DataBuffer): string =
## convert buffer to string type using copy
##

data.toString()

converter toBuffer*(err: ref CatchableError): DataBuffer =
## convert exception to an object with StringBuffer
##

return DataBuffer.new(err.msg)
Loading