Reduce use of deprecatedCharacters in WebCore
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBKeyPath.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "IDBKeyPath.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "KeyedCoding.h"
32 #include <wtf/ASCIICType.h>
33 #include <wtf/dtoa.h>
34
35 namespace WebCore {
36
37 class IDBKeyPathLexer {
38 public:
39     enum TokenType {
40         TokenIdentifier,
41         TokenDot,
42         TokenEnd,
43         TokenError
44     };
45
46     explicit IDBKeyPathLexer(const String& s)
47         : m_string(s)
48         , m_remainingText(s)
49         , m_currentTokenType(TokenError)
50     {
51     }
52
53     TokenType currentTokenType() const { return m_currentTokenType; }
54
55     TokenType nextTokenType()
56     {
57         m_currentTokenType = lex(m_currentElement);
58         return m_currentTokenType;
59     }
60
61     const String& currentElement() { return m_currentElement; }
62
63 private:
64     TokenType lex(String&);
65     TokenType lexIdentifier(String&);
66
67     String m_currentElement;
68     const String m_string;
69     StringView m_remainingText;
70     TokenType m_currentTokenType;
71 };
72
73 IDBKeyPathLexer::TokenType IDBKeyPathLexer::lex(String& element)
74 {
75     if (m_remainingText.isEmpty())
76         return TokenEnd;
77
78     if (m_remainingText[0] == '.') {
79         m_remainingText = m_remainingText.substring(1);
80         return TokenDot;
81     }
82
83     return lexIdentifier(element);
84 }
85
86 namespace {
87
88 // The following correspond to grammar in ECMA-262.
89 const uint32_t unicodeLetter = U_GC_L_MASK | U_GC_NL_MASK;
90 const uint32_t unicodeCombiningMark = U_GC_MN_MASK | U_GC_MC_MASK;
91 const uint32_t unicodeDigit = U_GC_ND_MASK;
92 const uint32_t unicodeConnectorPunctuation = U_GC_PC_MASK;
93 const UChar ZWNJ = 0x200C;
94 const UChar ZWJ = 0x200D;
95
96 static inline bool isIdentifierStartCharacter(UChar c)
97 {
98     return (U_GET_GC_MASK(c) & unicodeLetter) || (c == '$') || (c == '_');
99 }
100
101 static inline bool isIdentifierCharacter(UChar c)
102 {
103     return (U_GET_GC_MASK(c) & (unicodeLetter | unicodeCombiningMark | unicodeDigit | unicodeConnectorPunctuation)) || (c == '$') || (c == '_') || (c == ZWNJ) || (c == ZWJ);
104 }
105
106 } // namespace
107
108 IDBKeyPathLexer::TokenType IDBKeyPathLexer::lexIdentifier(String& element)
109 {
110     StringView start = m_remainingText;
111     if (!m_remainingText.isEmpty() && isIdentifierStartCharacter(m_remainingText[0]))
112         m_remainingText = m_remainingText.substring(1);
113     else
114         return TokenError;
115
116     while (!m_remainingText.isEmpty() && isIdentifierCharacter(m_remainingText[0]))
117         m_remainingText = m_remainingText.substring(1);
118
119     element = start.substring(0, start.length() - m_remainingText.length()).toString();
120     return TokenIdentifier;
121 }
122
123 static bool IDBIsValidKeyPath(const String& keyPath)
124 {
125     IDBKeyPathParseError error;
126     Vector<String> keyPathElements;
127     IDBParseKeyPath(keyPath, keyPathElements, error);
128     return error == IDBKeyPathParseErrorNone;
129 }
130
131 void IDBParseKeyPath(const String& keyPath, Vector<String>& elements, IDBKeyPathParseError& error)
132 {
133     // IDBKeyPath ::= EMPTY_STRING | identifier ('.' identifier)*
134     // The basic state machine is:
135     //   Start => {Identifier, End}
136     //   Identifier => {Dot, End}
137     //   Dot => {Identifier}
138     // It bails out as soon as it finds an error, but doesn't discard the bits it managed to parse.
139     enum ParserState { Identifier, Dot, End };
140
141     IDBKeyPathLexer lexer(keyPath);
142     IDBKeyPathLexer::TokenType tokenType = lexer.nextTokenType();
143     ParserState state;
144     if (tokenType == IDBKeyPathLexer::TokenIdentifier)
145         state = Identifier;
146     else if (tokenType == IDBKeyPathLexer::TokenEnd)
147         state = End;
148     else {
149         error = IDBKeyPathParseErrorStart;
150         return;
151     }
152
153     while (1) {
154         switch (state) {
155         case Identifier : {
156             IDBKeyPathLexer::TokenType tokenType = lexer.currentTokenType();
157             ASSERT(tokenType == IDBKeyPathLexer::TokenIdentifier);
158
159             String element = lexer.currentElement();
160             elements.append(element);
161
162             tokenType = lexer.nextTokenType();
163             if (tokenType == IDBKeyPathLexer::TokenDot)
164                 state = Dot;
165             else if (tokenType == IDBKeyPathLexer::TokenEnd)
166                 state = End;
167             else {
168                 error = IDBKeyPathParseErrorIdentifier;
169                 return;
170             }
171             break;
172         }
173         case Dot: {
174             IDBKeyPathLexer::TokenType tokenType = lexer.currentTokenType();
175             ASSERT(tokenType == IDBKeyPathLexer::TokenDot);
176
177             tokenType = lexer.nextTokenType();
178             if (tokenType == IDBKeyPathLexer::TokenIdentifier)
179                 state = Identifier;
180             else {
181                 error = IDBKeyPathParseErrorDot;
182                 return;
183             }
184             break;
185         }
186         case End: {
187             error = IDBKeyPathParseErrorNone;
188             return;
189         }
190         }
191     }
192 }
193
194 IDBKeyPath::IDBKeyPath(const String& string)
195     : m_type(StringType)
196     , m_string(string)
197 {
198     ASSERT(!m_string.isNull());
199 }
200
201 IDBKeyPath::IDBKeyPath(const Vector<String>& array)
202     : m_type(ArrayType)
203     , m_array(array)
204 {
205 #ifndef NDEBUG
206     for (size_t i = 0; i < m_array.size(); ++i)
207         ASSERT(!m_array[i].isNull());
208 #endif
209 }
210
211 bool IDBKeyPath::isValid() const
212 {
213     switch (m_type) {
214     case NullType:
215         return false;
216
217     case StringType:
218         return IDBIsValidKeyPath(m_string);
219
220     case ArrayType:
221         if (m_array.isEmpty())
222             return false;
223         for (size_t i = 0; i < m_array.size(); ++i) {
224             if (!IDBIsValidKeyPath(m_array[i]))
225                 return false;
226         }
227         return true;
228     }
229     ASSERT_NOT_REACHED();
230     return false;
231 }
232
233 bool IDBKeyPath::operator==(const IDBKeyPath& other) const
234 {
235     if (m_type != other.m_type)
236         return false;
237
238     switch (m_type) {
239     case NullType:
240         return true;
241     case StringType:
242         return m_string == other.m_string;
243     case ArrayType:
244         return m_array == other.m_array;
245     }
246     ASSERT_NOT_REACHED();
247     return false;
248 }
249
250 IDBKeyPath IDBKeyPath::isolatedCopy() const
251 {
252     IDBKeyPath result;
253     result.m_type = m_type;
254     result.m_string = m_string.isolatedCopy();
255
256     result.m_array.reserveInitialCapacity(m_array.size());
257     for (size_t i = 0; i < m_array.size(); ++i)
258         result.m_array.uncheckedAppend(m_array[i].isolatedCopy());
259
260     return result;
261 }
262
263 void IDBKeyPath::encode(KeyedEncoder& encoder) const
264 {
265     encoder.encodeEnum("type", m_type);
266     switch (m_type) {
267     case IDBKeyPath::NullType:
268         break;
269     case IDBKeyPath::StringType:
270         encoder.encodeString("string", m_string);
271         break;
272     case IDBKeyPath::ArrayType:
273         encoder.encodeObjects("array", m_array.begin(), m_array.end(), [](WebCore::KeyedEncoder& encoder, const String& string) {
274             encoder.encodeString("string", string);
275         });
276         break;
277     default:
278         ASSERT_NOT_REACHED();
279     };
280 }
281
282 bool IDBKeyPath::decode(KeyedDecoder& decoder, IDBKeyPath& result)
283 {
284     auto enumFunction = [](int64_t value) {
285         return value == NullType || value == StringType || value == ArrayType;
286     };
287
288     if (!decoder.decodeVerifiedEnum("type", result.m_type, enumFunction))
289         return false;
290
291     if (result.m_type == NullType)
292         return true;
293
294     if (result.m_type == StringType)
295         return decoder.decodeString("string", result.m_string);
296
297     ASSERT(result.m_type == ArrayType);
298
299     auto arrayFunction = [](KeyedDecoder& decoder, String& result) {
300         return decoder.decodeString("string", result);
301     };
302
303     return decoder.decodeObjects("array", result.m_array, arrayFunction);
304 }
305
306 } // namespace WebCore
307
308 #endif // ENABLE(INDEXED_DATABASE)