about summary refs log tree commit diff stats
path: root/adapter/img/jebp.nim
blob: 7a10a27ead47ce082ccb167bc111f276e80772e9 (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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import std/options
import std/os
import std/posix
import std/strutils

import utils/sandbox
import utils/twtstr

{.compile: "jebp.c".}

when sizeof(cint) < 4:
  type jebp_int = clong
else:
  type jebp_int = cint

{.passc: "-I" & currentSourcePath().parentDir().}

const STDIN_FILENO = 0
const STDOUT_FILENO = 1

{.push header: "jebp.h".}
type
  jebp_io_callbacks {.importc.} = object
    read: proc(data: pointer; size: csize_t; user: pointer): csize_t {.cdecl.}
    check_error: proc(user: pointer): cint {.cdecl.}

  jebp_error_t = cint

  jebp_color_t = object
    r: uint8
    g: uint8
    b: uint8
    a: uint8

  jebp_image_t {.importc.} = object
    width: jebp_int
    height: jebp_int
    pixels: ptr jebp_color_t

proc jebp_read_from_callbacks(image: ptr jebp_image_t;
  cb: ptr jebp_io_callbacks; user: pointer): jebp_error_t {.importc.}

proc jebp_read_size_from_callbacks(image: ptr jebp_image_t;
  cb: ptr jebp_io_callbacks; user: pointer): jebp_error_t {.importc.}

proc jebp_error_string(err: jebp_error_t): cstring {.importc.}

proc jebp_free_image(image: ptr jebp_image_t) {.importc.}
{.pop.}

proc myRead(data: pointer; size: csize_t; user: pointer): csize_t {.cdecl.} =
  var n = csize_t(0)
  while n < size:
    let i = read(STDIN_FILENO, addr cast[ptr UncheckedArray[char]](data)[n],
      int(size - n))
    if i == 0:
      break
    n += csize_t(i)
  return n

proc writeAll(data: pointer; size: int) =
  var n = 0
  while n < size:
    let i = write(STDOUT_FILENO, addr cast[ptr UncheckedArray[uint8]](data)[n],
      int(size) - n)
    assert i >= 0
    n += i

proc puts(s: string) =
  if s.len > 0:
    writeAll(unsafeAddr s[0], s.len)

proc die(s: string) {.noreturn.} =
  puts(s)
  quit(1)

proc main() =
  enterNetworkSandbox()
  let scheme = getEnv("MAPPED_URI_SCHEME")
  let f = scheme.after('+')
  case getEnv("MAPPED_URI_PATH")
  of "decode":
    if f != "webp":
      die("Cha-Control: ConnectionError 1 unknown format " & f)
    let headers = getEnv("REQUEST_HEADERS")
    var targetWidth = cint(-1)
    var targetHeight = cint(-1)
    var infoOnly = false
    for hdr in headers.split('\n'):
      let v = hdr.after(':').strip()
      case hdr.until(':')
      of "Cha-Image-Info-Only":
        infoOnly = v == "1"
      of "Cha-Image-Target-Dimensions":
        let s = v.split('x')
        if s.len != 2:
          die("Cha-Control: ConnectionError 1 wrong dimensions\n")
        let w = parseUInt32(s[0], allowSign = false)
        let h = parseUInt32(s[1], allowSign = false)
        if w.isNone or w.isNone:
          die("Cha-Control: ConnectionError 1 wrong dimensions\n")
        targetWidth = cint(w.get)
        targetHeight = cint(h.get)
    var image = jebp_image_t()
    var cb = jebp_io_callbacks(read: myRead)
    if infoOnly:
      let res = jebp_read_size_from_callbacks(addr image, addr cb, nil)
      if res == 0:
        puts("Cha-Image-Dimensions: " & $image.width & "x" &
          $image.height & "\n\n")
        quit(0)
      else:
        die("Cha-Control: ConnectionError 1 jepb error " &
          $jebp_error_string(res))
    let res = jebp_read_from_callbacks(addr image, addr cb, nil)
    if res != 0:
      die("Cha-Control: ConnectionError 1 jebp error " &
        $jebp_error_string(res))
    else:
      puts("Cha-Image-Dimensions: " & $image.width & "x" & $image.height &
        "\n\n")
      writeAll(image.pixels, image.width * image.height * 4)
      jebp_free_image(addr image)
  of "encode":
    die("Cha-Control: ConnectionError 1 not supported")

main()