Skip to content
File

Blob: src/workerd/api/encoding.h

cpp180 lines
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#pragma once
6 
7#include "encoding-legacy.h"
8#include "encoding-shared.h"
9 
10#include <workerd/io/compatibility-date.capnp.h>
11#include <workerd/jsg/jsg.h>
12 
13#include <unicode/ucnv.h>
14 
15namespace workerd::api {
16 
17// Decoder implementation that uses ICU's built-in conversion APIs.
18// ICU's decoder is fairly comprehensive, covering the full range
19// of encodings required by the Encoding specification.
20class IcuDecoder final: public Decoder {
21 public:
22 IcuDecoder(Encoding encoding, UConverter* converter, bool fatal, bool ignoreBom)
23 : encoding(encoding),
24 inner(converter),
25 fatal(fatal),
26 ignoreBom(ignoreBom),
27 bomSeen(false) {}
28 IcuDecoder(IcuDecoder&&) = default;
29 IcuDecoder& operator=(IcuDecoder&&) = default;
30 
31 static kj::Maybe<IcuDecoder> create(Encoding encoding, bool fatal, bool ignoreBom);
32 
33 Encoding getEncoding() override {
34 return encoding;
35 }
36 
37 kj::Maybe<jsg::JsString> decode(
38 jsg::Lock& js, kj::ArrayPtr<const kj::byte> buffer, bool flush = false) override;
39 
40 void reset() override;
41 
42 private:
43 struct ConverterDeleter {
44 void operator()(UConverter* pointer) const {
45 ucnv_close(pointer);
46 }
47 };
48 
49 Encoding encoding;
50 std::unique_ptr<UConverter, ConverterDeleter> inner;
51 
52 bool fatal;
53 bool ignoreBom;
54 bool bomSeen;
55};
56 
57// Implements the TextDecoder interface as prescribed by:
58// https://encoding.spec.whatwg.org/#interface-textdecoder
59class TextDecoder final: public jsg::Object {
60 public:
61 using DecoderImpl = kj::OneOf<LegacyDecoder, IcuDecoder>;
62 
63 struct ConstructorOptions {
64 bool fatal = false;
65 bool ignoreBOM = false;
66 
67 JSG_STRUCT(fatal, ignoreBOM);
68 };
69 
70 struct DecodeOptions {
71 bool stream = false;
72 
73 JSG_STRUCT(stream);
74 };
75 
76 static jsg::Ref<TextDecoder> constructor(
77 jsg::Lock& js, jsg::Optional<kj::String> label, jsg::Optional<ConstructorOptions> options);
78 
79 jsg::JsString decode(jsg::Lock& js,
80 jsg::Optional<kj::Array<const kj::byte>> input,
81 jsg::Optional<DecodeOptions> options);
82 
83 kj::StringPtr getEncoding();
84 
85 bool getFatal() {
86 return ctorOptions.fatal;
87 }
88 bool getIgnoreBom() {
89 return ctorOptions.ignoreBOM;
90 }
91 
92 JSG_RESOURCE_TYPE(TextDecoder, CompatibilityFlags::Reader flags) {
93 JSG_METHOD(decode);
94 if (flags.getJsgPropertyOnPrototypeTemplate()) {
95 JSG_READONLY_PROTOTYPE_PROPERTY(encoding, getEncoding);
96 JSG_READONLY_PROTOTYPE_PROPERTY(fatal, getFatal);
97 JSG_READONLY_PROTOTYPE_PROPERTY(ignoreBOM, getIgnoreBom);
98 } else {
99 JSG_READONLY_INSTANCE_PROPERTY(encoding, getEncoding);
100 JSG_READONLY_INSTANCE_PROPERTY(fatal, getFatal);
101 JSG_READONLY_INSTANCE_PROPERTY(ignoreBOM, getIgnoreBom);
102 }
103 // TODO(soon): Defining the constructor override here *should not* be
104 // necessary but for some reason the type generation is creating an
105 // invalid result without it.
106 JSG_TS_OVERRIDE({
107 constructor(label?: string, options?: TextDecoderConstructorOptions);
108 });
109 }
110 
111 explicit TextDecoder(DecoderImpl decoder): decoder(kj::mv(decoder)) {}
112 
113 explicit TextDecoder(DecoderImpl decoder, const ConstructorOptions& options)
114 : decoder(kj::mv(decoder)),
115 ctorOptions(options) {}
116 
117 kj::Maybe<jsg::JsString> decodePtr(
118 jsg::Lock& js, kj::ArrayPtr<const kj::byte> buffer, bool flush);
119 
120 private:
121 Decoder& getImpl();
122 
123 DecoderImpl decoder;
124 ConstructorOptions ctorOptions;
125 
126 static const DecodeOptions DEFAULT_OPTIONS;
127 static constexpr kj::byte DUMMY = 0;
128 static const kj::Array<const kj::byte> EMPTY;
129};
130 
131// Implements the TextEncoder interface as prescribed by:
132// https://encoding.spec.whatwg.org/#interface-textencoder
133class TextEncoder final: public jsg::Object {
134 public:
135 struct EncodeIntoResult {
136 int read;
137 int written;
138 // TODO(conform): Perhaps use unsigned long long.
139 
140 JSG_STRUCT(read, written);
141 };
142 
143 static jsg::Ref<TextEncoder> constructor(jsg::Lock& js);
144 
145 jsg::JsUint8Array encode(jsg::Lock& js, jsg::Optional<jsg::JsString> input);
146 
147 EncodeIntoResult encodeInto(jsg::Lock& js, jsg::JsString input, jsg::JsUint8Array buffer);
148 
149 // UTF-8 is the only encoding type supported by the WHATWG spec.
150 kj::StringPtr getEncoding() {
151 return "utf-8";
152 }
153 
154 JSG_RESOURCE_TYPE(TextEncoder, CompatibilityFlags::Reader flags) {
155 JSG_METHOD(encode);
156 JSG_METHOD(encodeInto);
157 if (flags.getJsgPropertyOnPrototypeTemplate()) {
158 JSG_READONLY_PROTOTYPE_PROPERTY(encoding, getEncoding);
159 } else {
160 JSG_READONLY_INSTANCE_PROPERTY(encoding, getEncoding);
161 }
162 
163 JSG_TS_OVERRIDE({
164 encodeInto(input: string, buffer: Uint8Array): TextEncoderEncodeIntoResult;
165 });
166 }
167};
168 
169#define EW_ENCODING_ISOLATE_TYPES \
170 api::TextDecoder, api::TextEncoder, api::TextDecoder::ConstructorOptions, \
171 api::TextDecoder::DecodeOptions, api::TextEncoder::EncodeIntoResult
172 
173namespace test {
174 
175size_t bestFit(const char* str, size_t bufferSize);
176size_t bestFit(const char16_t* str, size_t bufferSize);
177 
178} // namespace test
179} // namespace workerd::api