1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
import std/options
import std/posix
import std/strutils
import monoucha/fromjs
import monoucha/javascript
import monoucha/jstypes
import utils/mimeguess
type
DeallocFun = proc(opaque, p: pointer) {.nimcall, raises: [].}
Blob* = ref object of RootObj
size* {.jsget.}: uint64
ctype* {.jsget: "type".}: string
buffer*: pointer
opaque*: pointer
deallocFun*: DeallocFun
fd*: Option[FileHandle]
WebFile* = ref object of Blob
webkitRelativePath {.jsget.}: string
name* {.jsget.}: string
jsDestructor(Blob)
jsDestructor(WebFile)
proc newBlob*(buffer: pointer; size: int; ctype: string;
deallocFun: DeallocFun; opaque: pointer = nil): Blob =
return Blob(
buffer: buffer,
size: uint64(size),
ctype: ctype,
deallocFun: deallocFun,
opaque: opaque
)
proc deallocBlob*(opaque, p: pointer) =
if p != nil:
dealloc(p)
proc finalize(blob: Blob) {.jsfin.} =
if blob.fd.isSome:
discard close(blob.fd.get)
if blob.deallocFun != nil:
blob.deallocFun(blob.opaque, blob.buffer)
blob.buffer = nil
proc finalize(file: WebFile) {.jsfin.} =
Blob(file).finalize()
proc newWebFile*(name: string; fd: FileHandle): WebFile =
return WebFile(
name: name,
fd: some(fd),
ctype: DefaultGuess.guessContentType(name)
)
type
BlobPropertyBag = object of JSDict
`type` {.jsdefault.}: string
#TODO endings
FilePropertyBag = object of BlobPropertyBag
#TODO lastModified: int64
proc newWebFile(ctx: JSContext; fileBits: seq[string]; fileName: string;
options = FilePropertyBag()): WebFile {.jsctor.} =
let file = WebFile(
name: fileName
)
var len = 0
for blobPart in fileBits:
len += blobPart.len
let buffer = alloc(len)
file.buffer = buffer
file.deallocFun = deallocBlob
var buf = cast[ptr UncheckedArray[uint8]](file.buffer)
var i = 0
for blobPart in fileBits:
if blobPart.len > 0:
copyMem(addr buf[i], unsafeAddr blobPart[0], blobPart.len)
i += blobPart.len
file.size = uint64(len)
block ctype:
for c in options.`type`:
if c notin char(0x20)..char(0x7E):
break ctype
file.ctype &= c.toLowerAscii()
return file
#TODO File, Blob constructors
proc getSize*(this: Blob): uint64 =
if this.fd.isSome:
var statbuf: Stat
if fstat(this.fd.get, statbuf) < 0:
return 0
return uint64(statbuf.st_size)
return this.size
proc size*(this: WebFile): uint64 {.jsfget.} =
return this.getSize()
#TODO lastModified
proc addBlobModule*(ctx: JSContext) =
let blobCID = ctx.registerType(Blob)
ctx.registerType(WebFile, parent = blobCID, name = "File")
|