about summary refs log tree commit diff stats
path: root/src/types/blob.nim
blob: c0278bdedfed6522501fd50d117916ec6684f3c1 (plain) (blame)
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")