diff --git a/async_postgres/pg_types/encoding.nim b/async_postgres/pg_types/encoding.nim index a1b9407..257868a 100644 --- a/async_postgres/pg_types/encoding.nim +++ b/async_postgres/pg_types/encoding.nim @@ -23,6 +23,11 @@ template writeBE64*(buf: var openArray[byte], pos: int, v: int64) = buf[pos + 6] = byte((v shr 8) and 0xFF) buf[pos + 7] = byte(v and 0xFF) +template writeBytesAt*(dst: var openArray[byte], pos: int, src: openArray[byte]) = + ## Copy src bytes into dst starting at pos. No-op when src is empty. + if src.len > 0: + copyMem(addr dst[pos], addr src[0], src.len) + proc toPgParamInline*(v: int16): PgParamInline = result.oid = OidInt2 result.format = 1 @@ -276,9 +281,8 @@ proc encodeBinaryArray*(elemOid: int32, elements: seq[seq[byte]]): seq[byte] = for e in elements: result.writeBE32(pos, int32(e.len)) pos += 4 - if e.len > 0: - copyMem(addr result[pos], addr e[0], e.len) - pos += e.len + result.writeBytesAt(pos, e) + pos += e.len proc encodeBinaryArray*(elemOid: int32, elements: seq[Option[seq[byte]]]): seq[byte] = ## Encode a 1-dimensional binary array that may contain NULL elements. @@ -315,9 +319,8 @@ proc encodeBinaryArray*(elemOid: int32, elements: seq[Option[seq[byte]]]): seq[b let ev = e.get result.writeBE32(pos, int32(ev.len)) pos += 4 - if ev.len > 0: - copyMem(addr result[pos], addr ev[0], ev.len) - pos += ev.len + result.writeBytesAt(pos, ev) + pos += ev.len proc encodeBinaryArrayEmpty*(elemOid: int32): seq[byte] = ## Encode an empty 1-dimensional PostgreSQL binary array. @@ -975,14 +978,15 @@ proc encodeHstoreBinary*(v: PgHstore): seq[byte] = result.writeBE32(pos, int32(k.len)) pos += 4 if k.len > 0: - copyMem(addr result[pos], addr k[0], k.len) + result.writeBytesAt(pos, k.toOpenArrayByte(0, k.high)) pos += k.len if val.isSome: - result.writeBE32(pos, int32(val.get.len)) + let vs = val.get + result.writeBE32(pos, int32(vs.len)) pos += 4 - if val.get.len > 0: - copyMem(addr result[pos], addr val.get[0], val.get.len) - pos += val.get.len + if vs.len > 0: + result.writeBytesAt(pos, vs.toOpenArrayByte(0, vs.high)) + pos += vs.len else: result.writeBE32(pos, -1'i32) pos += 4 diff --git a/async_postgres/pg_types/ranges.nim b/async_postgres/pg_types/ranges.nim index 2bf0b1f..3924cc1 100644 --- a/async_postgres/pg_types/ranges.nim +++ b/async_postgres/pg_types/ranges.nim @@ -338,18 +338,14 @@ proc encodeRangeBinaryImpl(r: RangeBinaryInput): seq[byte] = result[0] = flags var pos = 1 if r.hasLower: - let lb = toBE32(int32(r.lowerData.len)) - copyMem(addr result[pos], addr lb[0], 4) + result.writeBE32(pos, int32(r.lowerData.len)) pos += 4 - if r.lowerData.len > 0: - copyMem(addr result[pos], addr r.lowerData[0], r.lowerData.len) - pos += r.lowerData.len + result.writeBytesAt(pos, r.lowerData) + pos += r.lowerData.len if r.hasUpper: - let ub = toBE32(int32(r.upperData.len)) - copyMem(addr result[pos], addr ub[0], 4) + result.writeBE32(pos, int32(r.upperData.len)) pos += 4 - if r.upperData.len > 0: - copyMem(addr result[pos], addr r.upperData[0], r.upperData.len) + result.writeBytesAt(pos, r.upperData) # toPgParam for range types (text format) @@ -734,16 +730,13 @@ proc encodeMultirangeBinaryImpl(rangeData: seq[seq[byte]]): seq[byte] = for rd in rangeData: size += 4 + rd.len result = newSeq[byte](size) - let cnt = toBE32(int32(rangeData.len)) - copyMem(addr result[0], addr cnt[0], 4) + result.writeBE32(0, int32(rangeData.len)) var pos = 4 for rd in rangeData: - let rl = toBE32(int32(rd.len)) - copyMem(addr result[pos], addr rl[0], 4) + result.writeBE32(pos, int32(rd.len)) pos += 4 - if rd.len > 0: - copyMem(addr result[pos], addr rd[0], rd.len) - pos += rd.len + result.writeBytesAt(pos, rd) + pos += rd.len # Multirange toPgParam (text format) diff --git a/async_postgres/pg_types/user_types.nim b/async_postgres/pg_types/user_types.nim index 3f8911f..2feeb28 100644 --- a/async_postgres/pg_types/user_types.nim +++ b/async_postgres/pg_types/user_types.nim @@ -1,9 +1,7 @@ import std/[options, macros, strutils, typetraits] import ../pg_protocol -import ./core -import ./decoding -import ./accessors +import core, decoding, encoding, accessors # User-defined enum type support # @@ -262,25 +260,20 @@ proc encodeBinaryComposite*( if f.data.isSome: size += f.data.get.len result = newSeq[byte](size) - let nf = toBE32(int32(fields.len)) - copyMem(addr result[0], addr nf[0], 4) + result.writeBE32(0, int32(fields.len)) var pos = 4 for f in fields: - let oid = toBE32(f.oid) - copyMem(addr result[pos], addr oid[0], 4) + result.writeBE32(pos, f.oid) pos += 4 if f.data.isNone: - let nl = toBE32(-1'i32) - copyMem(addr result[pos], addr nl[0], 4) + result.writeBE32(pos, -1'i32) pos += 4 else: let data = f.data.get - let dl = toBE32(int32(data.len)) - copyMem(addr result[pos], addr dl[0], 4) + result.writeBE32(pos, int32(data.len)) pos += 4 - if data.len > 0: - copyMem(addr result[pos], addr data[0], data.len) - pos += data.len + result.writeBytesAt(pos, data) + pos += data.len proc compositeFieldToText(val: string): string = ## Escape a composite field value for text format output.