Skip to content
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

[WIP] new module jsarrays: ArrayBuffer, DataView + typed js API's #14128

Closed
Closed
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
78 changes: 78 additions & 0 deletions lib/js/jsarrays.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
##[
This module provides a wrapper for ArrayBuffer, DataView and related API's such
as `getInt8`, `setInt16`.
]##

#[
`BigInt` could be homed here.
]#

static: doAssert defined(js)

type
ArrayBuffer* = ref object {.importjs.}
DataView* = ref object {.importjs.}
## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView

proc newArrayBuffer*(n: int): ArrayBuffer {.importjs: "new ArrayBuffer(#)".}
proc newDataView*(a: ArrayBuffer, offset: int): DataView {.importjs: "new DataView(#, #)".}
proc toArrayBuffer*(a: string): ArrayBuffer =
let n = a.len
result = newArrayBuffer(n)
{.emit:"""
const view = new Uint8Array(`result`);
for(i=0;i<`n`;i++){
view[i] = `a`[i];
}
""".}

template genGetSet(T, funGet, funSet): untyped =
proc funGet(a: DataView, byteOffset: int, littleEndian: bool): T {.importcpp.}
proc funSet(a: DataView, byteOffset: int, value: T, littleEndian: bool): T {.importcpp.}

genGetSet int8, getInt8, setInt8
genGetSet int16, getInt16, setInt16
genGetSet int32, getInt32, setInt32

proc getTyped*(a: DataView, T: typedesc, offset: int, littleEndian: bool): T =
when false: discard
elif T is int8: getInt8(a, offset, littleEndian)
elif T is int16: getInt16(a, offset, littleEndian)
elif T is int32: getInt32(a, offset, littleEndian)
else: static doAssert false, $T # add as needed

when false: ## scratch below
# view[i] = str.charCodeAt(i); // check whether would be needed for cstring

# proc `[]=`(a: ArrayBuffer, index: int, val: char) =
# {.emit: """`a`[`index`] = `val`;""".}

# DataView.prototype.getBigInt64()
# DataView.prototype.getBigUint64()
# DataView.prototype.getFloat32()
# DataView.prototype.getFloat64()
# DataView.prototype.getInt16()
# DataView.prototype.getInt32()
# DataView.prototype.getInt8()
# DataView.prototype.getUint16()
# DataView.prototype.getUint32()
# DataView.prototype.getUint8()
# DataView.prototype.setBigInt64()
# DataView.prototype.setBigUint64()
# DataView.prototype.setFloat32()
# DataView.prototype.setFloat64()
# DataView.prototype.setInt16()
# DataView.prototype.setInt32()
# DataView.prototype.setInt8()
# DataView.prototype.setUint16()
# DataView.prototype.setUint32()
# DataView.prototype.setUint8()

proc decode() =
case bufLen
of int32.sizeof:
cast[ptr int](buffer)[] = s2.getInt32(0, true)
of int16.sizeof:
cast[ptr int16](buffer)[] = s2.getInt16(0, true)
else:
doAssert false
33 changes: 24 additions & 9 deletions lib/pure/streams.nim
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@

include "system/inclrtl"

when defined(js):
import std/jsarrays

const taintMode = compileOption("taintmode")

proc newEIO(msg: string): owned(ref IOError) =
Expand Down Expand Up @@ -393,8 +396,6 @@ proc writeLine*(s: Stream, args: varargs[string, `$`]) =

proc read*[T](s: Stream, result: var T) =
## Generic read procedure. Reads `result` from the stream `s`.
##
## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now.
runnableExamples:
var strm = newStringStream("012")
## readInt
Expand All @@ -412,8 +413,6 @@ proc read*[T](s: Stream, result: var T) =

proc peek*[T](s: Stream, result: var T) =
## Generic peek procedure. Peeks `result` from the stream `s`.
##
## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now.
runnableExamples:
var strm = newStringStream("012")
## peekInt
Expand Down Expand Up @@ -1134,6 +1133,10 @@ type
## This is updated when called `writeLine` etc.
pos: int

when defined js:
data2: ArrayBuffer
dataView: DataView

when (NimMajor, NimMinor) < (1, 3) and defined(js):
proc ssAtEnd(s: Stream): bool {.compileTime.} =
var s = StringStream(s)
Expand Down Expand Up @@ -1217,11 +1220,19 @@ else: # after 1.3 or JS not defined
result = min(bufLen, s.data.len - s.pos)
if result > 0:
when defined(js):
try:
cast[ptr string](buffer)[][0..<result] = s.data[s.pos..<s.pos+result]
except:
raise newException(Defect, "could not read string stream, " &
"did you use a non-string buffer pointer?", getCurrentException())
let view = s.dataView
template fun(T) =
cast[ptr T](buffer)[] = view.getTyped(T, 0, true)
case bufLen
of int8.sizeof: fun(int8)
of int32.sizeof: fun(int32)
of int16.sizeof: fun(int16)
else: doAssert false, $bufLen
# try:
# cast[ptr string](buffer)[][0..<result] = s.data[s.pos..<s.pos+result]
# except:
# raise newException(Defect, "could not read string stream, " &
# "did you use a non-string buffer pointer?", getCurrentException())
elif not defined(nimscript):
copyMem(buffer, addr(s.data[s.pos]), result)
inc(s.pos, result)
Expand Down Expand Up @@ -1285,6 +1296,10 @@ else: # after 1.3 or JS not defined

new(result)
result.data = s
when defined js:
# IMPROVE, move
result.data2 = s.toArrayBuffer
result.dataView = newDataView(result.data2, 0)
result.pos = 0
result.closeImpl = ssClose
result.atEndImpl = ssAtEnd
Expand Down
46 changes: 35 additions & 11 deletions tests/js/tstreams.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,39 @@ GROOT

import streams

var s = newStringStream("I\nAM\nGROOT")
doAssert s.peekStr(1) == "I"
doAssert s.peekChar() == 'I'
for line in s.lines:
echo line
s.close
block:
var s = newStringStream("I\nAM\nGROOT")
doAssert s.peekStr(1) == "I"
doAssert s.peekChar() == 'I'
for line in s.lines:
echo line
s.close

var s2 = newStringStream("abc")
doAssert s2.readAll == "abc"
s2.write("def")
doAssert s2.data == "abcdef"
s2.close
var s2 = newStringStream("abc")
doAssert s2.readAll == "abc"
s2.write("def")
doAssert s2.data == "abcdef"
s2.close


block:
proc fun[T](x: T) =
# todo: this could be done via `write` on a StringStream
var str: string
str.setLen 10
for i in 0..<T.sizeof: # improve
str[i] = cast[char](255 and (x shr (i*8)))

var s = newStringStream(str)
var x2: T
s.read(x2)
doAssert x2 == x
# echo (x, x2)

fun(234_560.int32)
fun(234.int16)
fun(0.int16)
fun(12.uint8)
fun(123123.int32)
fun(123123.uint32)
fun((-123).int8)