about summary refs log tree commit diff stats
path: root/src/types/blob.nim
blob: 63fb5b84ed0061d7b8cfb83c1d0ae875bfbc8514 (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
110
111
112
113
import std/options
import std/os
import std/strutils

import js/fromjs
import js/javascript
import js/jstypes
import utils/mimeguess
import utils/twtstr

type
  DeallocFun = proc() {.closure.}

  Blob* = ref object of RootObj
    isfile*: bool
    size* {.jsget.}: uint64
    ctype* {.jsget: "type".}: string
    buffer*: pointer
    deallocFun*: DeallocFun

  WebFile* = ref object of Blob
    webkitRelativePath {.jsget.}: string
    path*: string
    file: File #TODO maybe use fd?

jsDestructor(Blob)
jsDestructor(WebFile)

proc newBlob*(buffer: pointer, size: int, ctype: string,
    deallocFun: DeallocFun): Blob =
  return Blob(
    buffer: buffer,
    size: uint64(size),
    ctype: ctype,
    deallocFun: deallocFun
  )

proc finalize(blob: Blob) {.jsfin.} =
  if blob.deallocFun != nil and blob.buffer != nil:
    blob.deallocFun()
    blob.buffer = nil

proc finalize(file: WebFile) {.jsfin.} =
  if file.deallocFun != nil and file.buffer != nil:
    file.deallocFun()
    file.buffer = nil

proc newWebFile*(path: string, webkitRelativePath = ""): WebFile =
  var file: File
  if not open(file, path, fmRead):
    raise newException(IOError, "Failed to open file")
  return WebFile(
    isfile: true,
    path: path,
    file: file,
    ctype: guessContentType(path),
    webkitRelativePath: webkitRelativePath
  )

type
  BlobPropertyBag = object of JSDict
    `type`: 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(
    isfile: false,
    path: fileName,
  )
  var len = 0
  for blobPart in fileBits:
    len += blobPart.len
  let buffer = alloc(len)
  file.buffer = buffer
  file.deallocFun = proc() = dealloc(buffer)
  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.isfile:
    return uint64(WebFile(this).path.getFileSize())
  return this.size

proc size*(this: WebFile): uint64 {.jsfget.} =
  return this.getSize()

func name*(this: WebFile): string {.jsfget.} =
  if this.path.len > 0 and this.path[^1] != '/':
    return this.path.afterLast('/')
  return this.path.afterLast('/', 2)

#TODO lastModified

proc addBlobModule*(ctx: JSContext) =
  let blobCID = ctx.registerType(Blob)
  ctx.registerType(WebFile, parent = blobCID, name = "File")