1ea70ef53SMark Nunberg/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2ea70ef53SMark Nunberg/*
3ea70ef53SMark Nunberg*     Copyright 2015 Couchbase, Inc
4ea70ef53SMark Nunberg*
5ea70ef53SMark Nunberg*   Licensed under the Apache License, Version 2.0 (the "License");
6ea70ef53SMark Nunberg*   you may not use this file except in compliance with the License.
7ea70ef53SMark Nunberg*   You may obtain a copy of the License at
8ea70ef53SMark Nunberg*
9ea70ef53SMark Nunberg*       http://www.apache.org/licenses/LICENSE-2.0
10ea70ef53SMark Nunberg*
11ea70ef53SMark Nunberg*   Unless required by applicable law or agreed to in writing, software
12ea70ef53SMark Nunberg*   distributed under the License is distributed on an "AS IS" BASIS,
13ea70ef53SMark Nunberg*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14ea70ef53SMark Nunberg*   See the License for the specific language governing permissions and
15ea70ef53SMark Nunberg*   limitations under the License.
16ea70ef53SMark Nunberg*/
17ea70ef53SMark Nunberg
18e36b78e3SMark Nunberg#ifndef SUBDOC_UESCAPE_H
19e36b78e3SMark Nunberg#define SUBDOC_UESCAPE_H
20e36b78e3SMark Nunberg
21e36b78e3SMark Nunbergnamespace Subdoc {
22e36b78e3SMark Nunberg
23e36b78e3SMark Nunbergclass UescapeConverter {
24e36b78e3SMark Nunbergpublic:
25e36b78e3SMark Nunberg    class Status {
26e36b78e3SMark Nunberg    public:
27e36b78e3SMark Nunberg        enum Code {
28e36b78e3SMark Nunberg            SUCCESS,
29e36b78e3SMark Nunberg            INCOMPLETE_SURROGATE, // End of string encountered with incomplete surrogate
30e36b78e3SMark Nunberg            INVALID_SURROGATE, // Invalid surrogate pair
31e36b78e3SMark Nunberg            EMBEDDED_NUL, // found embedded 0x00 pair
32e36b78e3SMark Nunberg            INVALID_HEXCHARS,
33e36b78e3SMark Nunberg            INVALID_CODEPOINT
34e36b78e3SMark Nunberg        };
35e36b78e3SMark Nunberg
36e36b78e3SMark Nunberg        operator bool() const {
37e36b78e3SMark Nunberg            return m_code == SUCCESS;
38e36b78e3SMark Nunberg        }
39e36b78e3SMark Nunberg
40e36b78e3SMark Nunberg        Code code() const {
41e36b78e3SMark Nunberg            return m_code;
42e36b78e3SMark Nunberg        }
43e36b78e3SMark Nunberg
44e36b78e3SMark Nunberg        Status(Code code) : m_code(code) {}
45e36b78e3SMark Nunberg
46e36b78e3SMark Nunberg    private:
47e36b78e3SMark Nunberg        Code m_code;
48e36b78e3SMark Nunberg    };
49e36b78e3SMark Nunberg
50e36b78e3SMark Nunberg    UescapeConverter(const std::string& in, std::string& out)
51e36b78e3SMark Nunberg    : m_inbuf(in.c_str()), m_inlen(in.size()), m_out(out) {
52e36b78e3SMark Nunberg    }
53e36b78e3SMark Nunberg
54e36b78e3SMark Nunberg    UescapeConverter(const char *s, size_t n, std::string& out)
55e36b78e3SMark Nunberg    : m_inbuf(s), m_inlen(n), m_out(out) {
56e36b78e3SMark Nunberg    }
57e36b78e3SMark Nunberg
58e36b78e3SMark Nunberg    inline Status convert();
59e36b78e3SMark Nunberg
60e36b78e3SMark Nunberg    static Status convert(const char *s, size_t n, std::string& out) {
61e36b78e3SMark Nunberg        UescapeConverter conv(s, n, out);
62e36b78e3SMark Nunberg        return conv.convert();
63e36b78e3SMark Nunberg    }
64e36b78e3SMark Nunberg
65e36b78e3SMark Nunberg    static Status convert(const std::string& in, std::string &out) {
66e36b78e3SMark Nunberg        UescapeConverter conv(in, out);
67e36b78e3SMark Nunberg        return conv.convert();
68e36b78e3SMark Nunberg    }
69e36b78e3SMark Nunberg
70e36b78e3SMark Nunberg
71e36b78e3SMark Nunbergprivate:
72e36b78e3SMark Nunberg    inline bool is_uescape(size_t pos);
73e36b78e3SMark Nunberg    inline void append_utf8(char32_t pt);
74e36b78e3SMark Nunberg    inline Status handle_uescape(size_t pos);
75e36b78e3SMark Nunberg
76e36b78e3SMark Nunberg    const char *m_inbuf;
77e36b78e3SMark Nunberg    size_t m_inlen;
78e36b78e3SMark Nunberg    std::string& m_out;
79e36b78e3SMark Nunberg    char16_t last_codepoint = 0;
80e36b78e3SMark Nunberg};
81e36b78e3SMark Nunberg
82e36b78e3SMark NunbergUescapeConverter::Status
83e36b78e3SMark NunbergUescapeConverter::convert()
84e36b78e3SMark Nunberg{
85e36b78e3SMark Nunberg    for (size_t ii = 0; ii < m_inlen; ii++) {
86e36b78e3SMark Nunberg        if (is_uescape(ii)) {
87e36b78e3SMark Nunberg            Status st = handle_uescape(ii);
88e36b78e3SMark Nunberg            if (!st) {
89e36b78e3SMark Nunberg                return st;
90e36b78e3SMark Nunberg            }
91e36b78e3SMark Nunberg
92e36b78e3SMark Nunberg            // Skip over the 6-1 characters of {\,u,x,x,x,x}
93e36b78e3SMark Nunberg            ii += 5;
94e36b78e3SMark Nunberg        } else {
95e36b78e3SMark Nunberg            m_out += m_inbuf[ii];
96e36b78e3SMark Nunberg        }
97e36b78e3SMark Nunberg    }
98e36b78e3SMark Nunberg    if (last_codepoint) {
99e36b78e3SMark Nunberg        return Status::INCOMPLETE_SURROGATE;
100e36b78e3SMark Nunberg    }
101e36b78e3SMark Nunberg    return Status::SUCCESS;
102e36b78e3SMark Nunberg}
103e36b78e3SMark Nunberg
104e36b78e3SMark Nunbergbool
105e36b78e3SMark NunbergUescapeConverter::is_uescape(size_t pos)
106e36b78e3SMark Nunberg{
107e36b78e3SMark Nunberg    if (m_inbuf[pos] != '\\') {
108e36b78e3SMark Nunberg        return false;
109e36b78e3SMark Nunberg    }
110e36b78e3SMark Nunberg    if (pos == m_inlen - 1) {
111e36b78e3SMark Nunberg        return false;
112e36b78e3SMark Nunberg    }
113e36b78e3SMark Nunberg    if (m_inbuf[pos+1] == 'u') {
114e36b78e3SMark Nunberg        return true;
115e36b78e3SMark Nunberg    }
116e36b78e3SMark Nunberg    return false;
117e36b78e3SMark Nunberg}
118e36b78e3SMark Nunberg
119e36b78e3SMark Nunbergvoid
120e36b78e3SMark NunbergUescapeConverter::append_utf8(char32_t pt)
121e36b78e3SMark Nunberg{
122e36b78e3SMark Nunberg    if (pt < 0x80) {
123e36b78e3SMark Nunberg        m_out += static_cast<char>(pt);
124e36b78e3SMark Nunberg    } else if (pt < 0x800) {
125e36b78e3SMark Nunberg        // 110xxxxxx 10xxxxxx
126e36b78e3SMark Nunberg        m_out += static_cast<char>((pt >> 6) | 0xC0);
127e36b78e3SMark Nunberg        // Write the remaining 6 bytes, and set higher order 11
128e36b78e3SMark Nunberg        m_out += static_cast<char>((pt & 0x3F) | 0x80);
129e36b78e3SMark Nunberg    } else if (pt < 0x10000) {
130e36b78e3SMark Nunberg        // 1110xxxx 10xxxxxx 10xxxxxx
131e36b78e3SMark Nunberg        m_out += static_cast<char>((pt >> 12) | 0xE0);
132e36b78e3SMark Nunberg        m_out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
133e36b78e3SMark Nunberg        m_out += static_cast<char>((pt & 0x3F) | 0x80);
134e36b78e3SMark Nunberg    } else {
135e36b78e3SMark Nunberg        // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
136e36b78e3SMark Nunberg        m_out += static_cast<char>((pt >> 18) | 0xF0);
137e36b78e3SMark Nunberg        m_out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
138e36b78e3SMark Nunberg        m_out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
139e36b78e3SMark Nunberg        m_out += static_cast<char>((pt & 0x3F) | 0x80);
140e36b78e3SMark Nunberg    }
141e36b78e3SMark Nunberg}
142e36b78e3SMark Nunberg
143e36b78e3SMark NunbergUescapeConverter::Status
144e36b78e3SMark NunbergUescapeConverter::handle_uescape(size_t pos)
145e36b78e3SMark Nunberg{
146e36b78e3SMark Nunberg    pos += 2; // Swallow '\u'
147e36b78e3SMark Nunberg    if (m_inlen - pos < 4) {
148e36b78e3SMark Nunberg        return Status::INVALID_HEXCHARS; // too small
149e36b78e3SMark Nunberg    }
150e36b78e3SMark Nunberg
151e36b78e3SMark Nunberg    char16_t res = 0;
152e36b78e3SMark Nunberg
153e36b78e3SMark Nunberg    for (size_t ii = pos; ii < pos+4; ii++) {
154e36b78e3SMark Nunberg        char numbuf[2] = { m_inbuf[ii], 0 };
155e36b78e3SMark Nunberg        char *endptr = NULL;
156e36b78e3SMark Nunberg
157e36b78e3SMark Nunberg        long rv = strtol(numbuf, &endptr, 16);
158e36b78e3SMark Nunberg        if (endptr && *endptr != '\0') {
159e36b78e3SMark Nunberg            return Status::INVALID_HEXCHARS;
160e36b78e3SMark Nunberg        }
161e36b78e3SMark Nunberg        if (rv == 0 && res == 0) {
162e36b78e3SMark Nunberg            continue;
163e36b78e3SMark Nunberg        }
164e36b78e3SMark Nunberg        res <<= 4;
165e36b78e3SMark Nunberg        res |= rv;
166e36b78e3SMark Nunberg    }
167e36b78e3SMark Nunberg
168e36b78e3SMark Nunberg    // From RFC 2781:
169e36b78e3SMark Nunberg    // 2.2 Decoding UTF-16
170e36b78e3SMark Nunberg    //    1) If W1 < 0xD800 or W1 > 0xDFFF, the character value U is the value
171e36b78e3SMark Nunberg    //       of W1. Terminate.
172e36b78e3SMark Nunberg    //
173e36b78e3SMark Nunberg    //    2) Determine if W1 is between 0xD800 and 0xDBFF. If not, the sequence
174e36b78e3SMark Nunberg    //       is in error and no valid character can be obtained using W1.
175e36b78e3SMark Nunberg    //       Terminate.
176e36b78e3SMark Nunberg    //
177e36b78e3SMark Nunberg    //    3) If there is no W2 (that is, the sequence ends with W1), or if W2
178e36b78e3SMark Nunberg    //       is not between 0xDC00 and 0xDFFF, the sequence is in error.
179e36b78e3SMark Nunberg    //       Terminate.
180e36b78e3SMark Nunberg    //
181e36b78e3SMark Nunberg    //    4) Construct a 20-bit unsigned integer U', taking the 10 low-order
182e36b78e3SMark Nunberg    //       bits of W1 as its 10 high-order bits and the 10 low-order bits of
183e36b78e3SMark Nunberg    //       W2 as its 10 low-order bits.
184e36b78e3SMark Nunberg    //    5) Add 0x10000 to U' to obtain the character value U. Terminate.
185e36b78e3SMark Nunberg    if (res == 0x00) {
186e36b78e3SMark Nunberg        return Status::EMBEDDED_NUL;
187e36b78e3SMark Nunberg    } else if (last_codepoint) {
188e36b78e3SMark Nunberg        if (res < 0xDC00 || res > 0xDFFF) {
189e36b78e3SMark Nunberg            return Status::INVALID_SURROGATE; // error
190e36b78e3SMark Nunberg        }
191e36b78e3SMark Nunberg
192e36b78e3SMark Nunberg        char16_t w1 = last_codepoint;
193e36b78e3SMark Nunberg        char16_t w2 = res;
194e36b78e3SMark Nunberg
195e36b78e3SMark Nunberg        // 10 low bits of w1 as its 10 high bits
196e36b78e3SMark Nunberg        char32_t cp;
197e36b78e3SMark Nunberg        cp = (w1 & 0x3FF) << 10;
198e36b78e3SMark Nunberg        // 10 low bits of w2 as its 20 low bits
199e36b78e3SMark Nunberg        cp |= (w2 & 0x3FF);
200e36b78e3SMark Nunberg
201e36b78e3SMark Nunberg        // Add 0x10000
202e36b78e3SMark Nunberg        cp += 0x10000;
203e36b78e3SMark Nunberg        append_utf8(cp);
204e36b78e3SMark Nunberg        last_codepoint = 0;
205e36b78e3SMark Nunberg
206e36b78e3SMark Nunberg    } else if (res < 0xD800 || res > 0xDFFF) {
207e36b78e3SMark Nunberg        append_utf8(res);
208e36b78e3SMark Nunberg    } else if (res > 0xD7FF && res < 0xDC00) {
209e36b78e3SMark Nunberg        last_codepoint = res;
210e36b78e3SMark Nunberg    } else {
211e36b78e3SMark Nunberg        return Status::INVALID_CODEPOINT;
212e36b78e3SMark Nunberg    }
213e36b78e3SMark Nunberg
214e36b78e3SMark Nunberg    return Status::SUCCESS;
215e36b78e3SMark Nunberg}
216e36b78e3SMark Nunberg
217e36b78e3SMark Nunberg}
218e36b78e3SMark Nunberg#endif