142 lines
3.9 KiB
Nim
Raw Normal View History

2023-09-12 13:50:29 -06:00
import threading/smartptrs
import std/hashes
2023-09-18 13:46:58 -06:00
import pkg/stew/ptrops
2023-09-12 13:50:29 -06:00
export hashes
type
2023-09-20 19:30:23 -07:00
DataBufferOpt* = enum
dbNullTerminate
2023-09-12 13:50:29 -06:00
DataBufferHolder* = object
buf: ptr UncheckedArray[byte]
size: int
2023-09-20 19:30:23 -07:00
cap: int
2023-09-12 13:50:29 -06:00
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:
2023-09-14 17:47:37 -06:00
# echo "buffer: FREE: ", repr x.buf.pointer
2023-09-12 13:50:29 -06:00
deallocShared(x.buf)
2023-09-26 16:02:42 -07:00
proc len*(a: DataBuffer): int =
if a.isNil: 0 else: a[].size
proc capacity*(a: DataBuffer): int =
if a.isNil: 0 else: a[].cap
2023-09-12 13:50:29 -06:00
proc isNil*(a: DataBuffer): bool = smartptrs.isNil(a)
proc hash*(a: DataBuffer): Hash =
a[].buf.toOpenArray(0, a[].size-1).hash()
2023-09-20 23:16:24 -07:00
proc `[]`*(db: DataBuffer, idx: int): var byte =
2023-09-20 23:07:52 -07:00
if idx >= db.len():
raise newException(IndexDefect, "index out of bounds")
db[].buf[idx]
2023-09-20 19:48:31 -07:00
template `==`*[T: char | byte](a: DataBuffer, b: openArray[T]): bool =
if a.isNil: false
elif a[].size != b.len: false
else: a.hash() == b.hash()
2023-09-27 18:27:29 -07:00
proc new*(tp: type DataBuffer, size: int = 0): DataBuffer =
2023-09-20 19:30:23 -07:00
## allocate new buffer with given capacity
2023-09-12 13:50:29 -06:00
##
newSharedPtr(DataBufferHolder(
2023-09-27 18:27:29 -07:00
buf: cast[typeof(result[].buf)](allocShared0(size)),
size: size,
cap: size,
2023-09-12 13:50:29 -06:00
))
2023-09-20 19:30:23 -07:00
proc new*[T: byte | char](tp: type DataBuffer, data: openArray[T], opts: set[DataBufferOpt] = {}): DataBuffer =
2023-09-12 13:50:29 -06:00
## allocate new buffer and copies indata from openArray
##
2023-09-20 19:30:23 -07:00
let dataCap =
if dbNullTerminate in opts: data.len() + 1
else: data.len()
result = DataBuffer.new(dataCap)
2023-09-12 13:50:29 -06:00
if data.len() > 0:
2023-09-20 19:30:23 -07:00
copyMem(result[].buf, baseAddr data, data.len())
result[].size = data.len()
2023-09-21 19:19:29 -07:00
# proc new*(tp: type DataBuffer, data: pointer, first, last: int): DataBuffer =
# DataBuffer.new(toOpenArray(cast[ptr UncheckedArray[byte]](data), first, last))
2023-09-20 20:57:47 -07:00
2023-09-20 23:07:52 -07:00
proc baseAddr*(db: DataBuffer): pointer =
db[].buf
2023-09-20 19:30:23 -07:00
proc clear*(db: DataBuffer) =
zeroMem(db[].buf, db[].cap)
2023-09-20 19:48:31 -07:00
db[].size = 0
2023-09-12 13:50:29 -06:00
2023-09-20 18:08:27 -07:00
proc setData*[T: byte | char](db: DataBuffer, data: openArray[T]) =
## allocate new buffer and copies indata from openArray
##
2023-09-20 19:48:31 -07:00
if data.len() > db[].cap:
2023-09-20 18:08:27 -07:00
raise newException(IndexDefect, "data too large for buffer")
2023-09-20 19:30:23 -07:00
db.clear() # this is expensive, but we can optimize later
copyMem(db[].buf, baseAddr data, data.len())
db[].size = data.len()
2023-09-20 18:08:27 -07:00
2023-09-26 17:01:21 -07:00
proc toSeq*(self: DataBuffer): seq[byte] =
2023-09-12 13:50:29 -06:00
## convert buffer to a seq type using copy and either a byte or char
##
2023-09-13 14:41:01 -06:00
result = newSeq[byte](self.len)
if self.len() > 0:
2023-09-18 13:46:58 -06:00
copyMem(addr result[0], addr self[].buf[0], self.len)
2023-09-12 13:50:29 -06:00
proc `@`*(self: DataBuffer): seq[byte] =
## Convert a buffer to a seq type using copy and
## either a byte or char
##
self.toSeq()
2023-09-26 17:01:21 -07:00
proc toString*(data: DataBuffer): string =
2023-09-12 13:50:29 -06:00
## convert buffer to string type using copy
##
if data.isNil: return ""
result = newString(data.len())
if data.len() > 0:
2023-09-18 13:46:58 -06:00
copyMem(addr result[0], addr data[].buf[0], data.len)
2023-09-12 13:50:29 -06:00
2023-09-13 14:41:01 -06:00
proc `$`*(data: DataBuffer): string =
## convert buffer to string type using copy
##
data.toString()
2023-09-14 17:47:37 -06:00
2023-09-21 19:19:29 -07:00
proc `==`*(a, b: DataBuffer): bool =
2023-09-21 19:23:23 -07:00
if a.isNil and b.isNil: result = true
elif a.isNil or b.isNil: result = false
elif a[].size != b[].size: result = false
elif a[].buf == b[].buf: result = true
else: result = a.hash() == b.hash()
2023-09-21 19:19:29 -07:00
2023-09-14 17:47:37 -06:00
converter toBuffer*(err: ref CatchableError): DataBuffer =
## convert exception to an object with StringBuffer
##
return DataBuffer.new(err.msg)
2023-09-20 17:49:57 -07:00
template toOpenArray*[T: byte | char](data: var DataBuffer, t: typedesc[T]): var openArray[T] =
2023-09-20 17:49:57 -07:00
## get openArray from DataBuffer as char
##
## this is explicit since sqlite treats string differently from openArray[byte]
var bf = cast[ptr UncheckedArray[T]](data[].buf)
2023-09-20 17:49:57 -07:00
bf.toOpenArray(0, data[].size-1)
2023-09-27 18:48:48 -07:00
template toOpenArray*(data: var DataBuffer, first, last: int): var openArray[byte] =
toOpenArray(data, byte).toOpenArray(first, last)