@shopify/react-native-skia 2.0.7 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/CMakeLists.txt +20 -1
- package/cpp/api/JsiSkApi.h +3 -0
- package/cpp/api/JsiSkImageFilterFactory.h +478 -39
- package/cpp/api/JsiSkSkottie.h +590 -0
- package/cpp/api/JsiSkottieFactory.h +65 -0
- package/cpp/api/recorder/Command.h +1 -0
- package/cpp/api/recorder/Convertor.h +16 -0
- package/cpp/api/recorder/Drawings.h +23 -0
- package/cpp/api/recorder/JsiRecorder.h +6 -0
- package/cpp/api/recorder/RNRecorder.h +9 -0
- package/cpp/api/third_party/SkottieUtils.cpp +333 -0
- package/cpp/api/third_party/SkottieUtils.h +172 -0
- package/cpp/skia/modules/jsonreader/SkJSONReader.cpp +980 -0
- package/cpp/skia/modules/jsonreader/SkJSONReader.h +389 -0
- package/cpp/skia/modules/skottie/include/ExternalLayer.h +56 -0
- package/cpp/skia/modules/skottie/include/Skottie.h +313 -0
- package/cpp/skia/modules/skottie/include/SkottieProperty.h +190 -0
- package/cpp/skia/modules/skottie/include/SlotManager.h +113 -0
- package/cpp/skia/modules/skottie/include/TextShaper.h +200 -0
- package/cpp/skia/modules/skottie/src/SkottieValue.h +56 -0
- package/cpp/skia/modules/skottie/src/animator/Animator.h +89 -0
- package/cpp/skia/modules/skottie/src/text/Font.h +82 -0
- package/cpp/skia/modules/skottie/src/text/TextAdapter.h +155 -0
- package/cpp/skia/modules/skottie/src/text/TextAnimator.h +121 -0
- package/cpp/skia/modules/skottie/src/text/TextValue.h +28 -0
- package/cpp/skia/modules/sksg/include/SkSGClipEffect.h +61 -0
- package/cpp/skia/modules/sksg/include/SkSGColorFilter.h +135 -0
- package/cpp/skia/modules/sksg/include/SkSGDraw.h +57 -0
- package/cpp/skia/modules/sksg/include/SkSGEffectNode.h +50 -0
- package/cpp/skia/modules/sksg/include/SkSGGeometryEffect.h +181 -0
- package/cpp/skia/modules/sksg/include/SkSGGeometryNode.h +54 -0
- package/cpp/skia/modules/sksg/include/SkSGGradient.h +108 -0
- package/cpp/skia/modules/sksg/include/SkSGGroup.h +65 -0
- package/cpp/skia/modules/sksg/include/SkSGImage.h +59 -0
- package/cpp/skia/modules/sksg/include/SkSGInvalidationController.h +46 -0
- package/cpp/skia/modules/sksg/include/SkSGMaskEffect.h +65 -0
- package/cpp/skia/modules/sksg/include/SkSGMerge.h +74 -0
- package/cpp/skia/modules/sksg/include/SkSGNode.h +128 -0
- package/cpp/skia/modules/sksg/include/SkSGOpacityEffect.h +54 -0
- package/cpp/skia/modules/sksg/include/SkSGPaint.h +112 -0
- package/cpp/skia/modules/sksg/include/SkSGPath.h +68 -0
- package/cpp/skia/modules/sksg/include/SkSGPlane.h +47 -0
- package/cpp/skia/modules/sksg/include/SkSGRect.h +122 -0
- package/cpp/skia/modules/sksg/include/SkSGRenderEffect.h +283 -0
- package/cpp/skia/modules/sksg/include/SkSGRenderNode.h +157 -0
- package/cpp/skia/modules/sksg/include/SkSGScene.h +47 -0
- package/cpp/skia/modules/sksg/include/SkSGText.h +82 -0
- package/cpp/skia/modules/sksg/include/SkSGTransform.h +127 -0
- package/cpp/skia/src/base/SkArenaAlloc.h +371 -0
- package/lib/commonjs/dom/nodes/datatypes/Gradient.d.ts +15 -15
- package/lib/commonjs/dom/types/Drawings.d.ts +5 -1
- package/lib/commonjs/dom/types/Drawings.js.map +1 -1
- package/lib/commonjs/dom/types/NodeType.d.ts +2 -1
- package/lib/commonjs/dom/types/NodeType.js +2 -0
- package/lib/commonjs/dom/types/NodeType.js.map +1 -1
- package/lib/commonjs/renderer/__tests__/e2e/AdvancedImageFilters.spec.d.ts +1 -0
- package/lib/commonjs/renderer/__tests__/e2e/Camera.spec.d.ts +21 -0
- package/lib/commonjs/renderer/__tests__/e2e/LightingImageFilters.spec.d.ts +1 -0
- package/lib/commonjs/renderer/__tests__/e2e/Skottie.spec.d.ts +1 -0
- package/lib/commonjs/renderer/__tests__/setup.d.ts +5 -0
- package/lib/commonjs/renderer/components/Skottie.d.ts +4 -0
- package/lib/commonjs/renderer/components/Skottie.js +13 -0
- package/lib/commonjs/renderer/components/Skottie.js.map +1 -0
- package/lib/commonjs/renderer/components/index.d.ts +1 -0
- package/lib/commonjs/renderer/components/index.js +11 -0
- package/lib/commonjs/renderer/components/index.js.map +1 -1
- package/lib/commonjs/skia/types/ImageFilter/ImageFilterFactory.d.ts +252 -15
- package/lib/commonjs/skia/types/ImageFilter/ImageFilterFactory.js.map +1 -1
- package/lib/commonjs/skia/types/Matrix4.d.ts +11 -2
- package/lib/commonjs/skia/types/Matrix4.js +42 -1
- package/lib/commonjs/skia/types/Matrix4.js.map +1 -1
- package/lib/commonjs/skia/types/Recorder.d.ts +2 -1
- package/lib/commonjs/skia/types/Recorder.js.map +1 -1
- package/lib/commonjs/skia/types/Skia.d.ts +2 -0
- package/lib/commonjs/skia/types/Skia.js.map +1 -1
- package/lib/commonjs/skia/types/Skottie.d.ts +223 -0
- package/lib/commonjs/skia/types/Skottie.js +73 -0
- package/lib/commonjs/skia/types/Skottie.js.map +1 -0
- package/lib/commonjs/skia/types/index.d.ts +1 -0
- package/lib/commonjs/skia/types/index.js +11 -0
- package/lib/commonjs/skia/types/index.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkImageFilterFactory.d.ts +29 -12
- package/lib/commonjs/skia/web/JsiSkImageFilterFactory.js +88 -19
- package/lib/commonjs/skia/web/JsiSkImageFilterFactory.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkia.js +2 -0
- package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkottieAnimation.d.ts +59 -0
- package/lib/commonjs/skia/web/JsiSkottieAnimation.js +243 -0
- package/lib/commonjs/skia/web/JsiSkottieAnimation.js.map +1 -0
- package/lib/commonjs/skia/web/JsiSkottieFactory.d.ts +9 -0
- package/lib/commonjs/skia/web/JsiSkottieFactory.js +26 -0
- package/lib/commonjs/skia/web/JsiSkottieFactory.js.map +1 -0
- package/lib/commonjs/sksg/Elements.d.ts +2 -1
- package/lib/commonjs/sksg/Elements.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/Core.d.ts +4 -2
- package/lib/commonjs/sksg/Recorder/Core.js +1 -0
- package/lib/commonjs/sksg/Recorder/Core.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/Player.js +2 -0
- package/lib/commonjs/sksg/Recorder/Player.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.d.ts +2 -1
- package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.js +4 -0
- package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/Recorder.d.ts +2 -1
- package/lib/commonjs/sksg/Recorder/Recorder.js +6 -0
- package/lib/commonjs/sksg/Recorder/Recorder.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/Visitor.js +3 -0
- package/lib/commonjs/sksg/Recorder/Visitor.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/commands/Drawing.d.ts +2 -2
- package/lib/commonjs/sksg/Recorder/commands/Drawing.js +11 -4
- package/lib/commonjs/sksg/Recorder/commands/Drawing.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/commands/ImageFilters.js +1 -1
- package/lib/commonjs/sksg/Recorder/commands/ImageFilters.js.map +1 -1
- package/lib/module/dom/nodes/datatypes/Gradient.d.ts +15 -15
- package/lib/module/dom/types/Drawings.d.ts +5 -1
- package/lib/module/dom/types/Drawings.js.map +1 -1
- package/lib/module/dom/types/NodeType.d.ts +2 -1
- package/lib/module/dom/types/NodeType.js +2 -0
- package/lib/module/dom/types/NodeType.js.map +1 -1
- package/lib/module/renderer/__tests__/e2e/AdvancedImageFilters.spec.d.ts +1 -0
- package/lib/module/renderer/__tests__/e2e/Camera.spec.d.ts +21 -0
- package/lib/module/renderer/__tests__/e2e/LightingImageFilters.spec.d.ts +1 -0
- package/lib/module/renderer/__tests__/e2e/Skottie.spec.d.ts +1 -0
- package/lib/module/renderer/__tests__/setup.d.ts +5 -0
- package/lib/module/renderer/components/Skottie.d.ts +4 -0
- package/lib/module/renderer/components/Skottie.js +5 -0
- package/lib/module/renderer/components/Skottie.js.map +1 -0
- package/lib/module/renderer/components/index.d.ts +1 -0
- package/lib/module/renderer/components/index.js +1 -0
- package/lib/module/renderer/components/index.js.map +1 -1
- package/lib/module/skia/types/ImageFilter/ImageFilterFactory.d.ts +252 -15
- package/lib/module/skia/types/ImageFilter/ImageFilterFactory.js.map +1 -1
- package/lib/module/skia/types/Matrix4.d.ts +11 -2
- package/lib/module/skia/types/Matrix4.js +40 -0
- package/lib/module/skia/types/Matrix4.js.map +1 -1
- package/lib/module/skia/types/Recorder.d.ts +2 -1
- package/lib/module/skia/types/Recorder.js.map +1 -1
- package/lib/module/skia/types/Skia.d.ts +2 -0
- package/lib/module/skia/types/Skia.js.map +1 -1
- package/lib/module/skia/types/Skottie.d.ts +223 -0
- package/lib/module/skia/types/Skottie.js +74 -0
- package/lib/module/skia/types/Skottie.js.map +1 -0
- package/lib/module/skia/types/index.d.ts +1 -0
- package/lib/module/skia/types/index.js +1 -0
- package/lib/module/skia/types/index.js.map +1 -1
- package/lib/module/skia/web/JsiSkImageFilterFactory.d.ts +29 -12
- package/lib/module/skia/web/JsiSkImageFilterFactory.js +88 -19
- package/lib/module/skia/web/JsiSkImageFilterFactory.js.map +1 -1
- package/lib/module/skia/web/JsiSkia.js +2 -0
- package/lib/module/skia/web/JsiSkia.js.map +1 -1
- package/lib/module/skia/web/JsiSkottieAnimation.d.ts +59 -0
- package/lib/module/skia/web/JsiSkottieAnimation.js +236 -0
- package/lib/module/skia/web/JsiSkottieAnimation.js.map +1 -0
- package/lib/module/skia/web/JsiSkottieFactory.d.ts +9 -0
- package/lib/module/skia/web/JsiSkottieFactory.js +19 -0
- package/lib/module/skia/web/JsiSkottieFactory.js.map +1 -0
- package/lib/module/sksg/Elements.d.ts +2 -1
- package/lib/module/sksg/Elements.js.map +1 -1
- package/lib/module/sksg/Recorder/Core.d.ts +4 -2
- package/lib/module/sksg/Recorder/Core.js +1 -0
- package/lib/module/sksg/Recorder/Core.js.map +1 -1
- package/lib/module/sksg/Recorder/Player.js +3 -1
- package/lib/module/sksg/Recorder/Player.js.map +1 -1
- package/lib/module/sksg/Recorder/ReanimatedRecorder.d.ts +2 -1
- package/lib/module/sksg/Recorder/ReanimatedRecorder.js +4 -0
- package/lib/module/sksg/Recorder/ReanimatedRecorder.js.map +1 -1
- package/lib/module/sksg/Recorder/Recorder.d.ts +2 -1
- package/lib/module/sksg/Recorder/Recorder.js +6 -0
- package/lib/module/sksg/Recorder/Recorder.js.map +1 -1
- package/lib/module/sksg/Recorder/Visitor.js +3 -0
- package/lib/module/sksg/Recorder/Visitor.js.map +1 -1
- package/lib/module/sksg/Recorder/commands/Drawing.d.ts +2 -2
- package/lib/module/sksg/Recorder/commands/Drawing.js +9 -2
- package/lib/module/sksg/Recorder/commands/Drawing.js.map +1 -1
- package/lib/module/sksg/Recorder/commands/ImageFilters.js +1 -1
- package/lib/module/sksg/Recorder/commands/ImageFilters.js.map +1 -1
- package/lib/typescript/lib/commonjs/renderer/components/Skottie.d.ts +2 -0
- package/lib/typescript/lib/commonjs/skia/types/Matrix4.d.ts +1 -0
- package/lib/typescript/lib/commonjs/skia/types/Skottie.d.ts +6 -0
- package/lib/typescript/lib/commonjs/skia/web/JsiSkImageFilterFactory.d.ts +22 -5
- package/lib/typescript/lib/commonjs/skia/web/JsiSkia.d.ts +2 -0
- package/lib/typescript/lib/commonjs/skia/web/JsiSkottieAnimation.d.ts +48 -0
- package/lib/typescript/lib/commonjs/skia/web/JsiSkottieFactory.d.ts +6 -0
- package/lib/typescript/lib/commonjs/sksg/Recorder/ReanimatedRecorder.d.ts +1 -0
- package/lib/typescript/lib/commonjs/sksg/Recorder/Recorder.d.ts +1 -0
- package/lib/typescript/lib/commonjs/sksg/Recorder/commands/Drawing.d.ts +1 -1
- package/lib/typescript/lib/module/mock/index.d.ts +7 -0
- package/lib/typescript/lib/module/renderer/components/Skottie.d.ts +2 -0
- package/lib/typescript/lib/module/renderer/components/index.d.ts +1 -0
- package/lib/typescript/lib/module/skia/Skia.web.d.ts +1 -0
- package/lib/typescript/lib/module/skia/types/Matrix4.d.ts +1 -0
- package/lib/typescript/lib/module/skia/types/Skottie.d.ts +5 -0
- package/lib/typescript/lib/module/skia/types/index.d.ts +1 -0
- package/lib/typescript/lib/module/skia/web/JsiSkImageFilterFactory.d.ts +22 -5
- package/lib/typescript/lib/module/skia/web/JsiSkia.d.ts +2 -0
- package/lib/typescript/lib/module/skia/web/JsiSkottieAnimation.d.ts +47 -0
- package/lib/typescript/lib/module/skia/web/JsiSkottieFactory.d.ts +5 -0
- package/lib/typescript/lib/module/sksg/Recorder/ReanimatedRecorder.d.ts +1 -0
- package/lib/typescript/lib/module/sksg/Recorder/Recorder.d.ts +1 -0
- package/lib/typescript/lib/module/sksg/Recorder/commands/Drawing.d.ts +1 -1
- package/lib/typescript/src/dom/nodes/datatypes/Gradient.d.ts +15 -15
- package/lib/typescript/src/dom/types/Drawings.d.ts +5 -1
- package/lib/typescript/src/dom/types/NodeType.d.ts +2 -1
- package/lib/typescript/src/renderer/__tests__/e2e/AdvancedImageFilters.spec.d.ts +1 -0
- package/lib/typescript/src/renderer/__tests__/e2e/Camera.spec.d.ts +21 -0
- package/lib/typescript/src/renderer/__tests__/e2e/LightingImageFilters.spec.d.ts +1 -0
- package/lib/typescript/src/renderer/__tests__/e2e/Skottie.spec.d.ts +1 -0
- package/lib/typescript/src/renderer/__tests__/setup.d.ts +5 -0
- package/lib/typescript/src/renderer/components/Skottie.d.ts +4 -0
- package/lib/typescript/src/renderer/components/index.d.ts +1 -0
- package/lib/typescript/src/skia/types/ImageFilter/ImageFilterFactory.d.ts +252 -15
- package/lib/typescript/src/skia/types/Matrix4.d.ts +11 -2
- package/lib/typescript/src/skia/types/Recorder.d.ts +2 -1
- package/lib/typescript/src/skia/types/Skia.d.ts +2 -0
- package/lib/typescript/src/skia/types/Skottie.d.ts +223 -0
- package/lib/typescript/src/skia/types/index.d.ts +1 -0
- package/lib/typescript/src/skia/web/JsiSkImageFilterFactory.d.ts +29 -12
- package/lib/typescript/src/skia/web/JsiSkottieAnimation.d.ts +59 -0
- package/lib/typescript/src/skia/web/JsiSkottieFactory.d.ts +9 -0
- package/lib/typescript/src/sksg/Elements.d.ts +2 -1
- package/lib/typescript/src/sksg/Recorder/Core.d.ts +4 -2
- package/lib/typescript/src/sksg/Recorder/ReanimatedRecorder.d.ts +2 -1
- package/lib/typescript/src/sksg/Recorder/Recorder.d.ts +2 -1
- package/lib/typescript/src/sksg/Recorder/commands/Drawing.d.ts +2 -2
- package/libs/android/arm64-v8a/libjsonreader.a +0 -0
- package/libs/android/armeabi-v7a/libjsonreader.a +0 -0
- package/libs/android/x86/libjsonreader.a +0 -0
- package/libs/android/x86_64/libjsonreader.a +0 -0
- package/libs/apple/libpathops.xcframework/Info.plist +8 -8
- package/libs/apple/libskia.xcframework/Info.plist +14 -14
- package/libs/apple/libskottie.xcframework/Info.plist +14 -14
- package/libs/apple/libskparagraph.xcframework/Info.plist +16 -16
- package/libs/apple/libsksg.xcframework/Info.plist +5 -5
- package/libs/apple/libskshaper.xcframework/Info.plist +14 -14
- package/libs/apple/libskunicode_libgrapheme.xcframework/Info.plist +14 -14
- package/libs/apple/libsvg.xcframework/Info.plist +14 -14
- package/package.json +1 -1
- package/react-native-skia.podspec +4 -2
- package/src/__tests__/snapshots/matrix4/camera-corner.png +0 -0
- package/src/__tests__/snapshots/matrix4/camera-offset.png +0 -0
- package/src/__tests__/snapshots/matrix4/camera-top-left-center.png +0 -0
- package/src/__tests__/snapshots/matrix4/camera-zoom-out.png +0 -0
- package/src/__tests__/snapshots/matrix4/full-rect.png +0 -0
- package/src/__tests__/snapshots/matrix4/rect.png +0 -0
- package/src/__tests__/snapshots/matrix4/scaled-rect.png +0 -0
- package/src/__tests__/snapshots/matrix4/test-perspective.png +0 -0
- package/src/__tests__/snapshots/matrix4/test-perspective2.png +0 -0
- package/src/dom/types/Drawings.ts +6 -0
- package/src/dom/types/NodeType.ts +2 -0
- package/src/renderer/__tests__/e2e/AdvancedImageFilters.spec.tsx +492 -0
- package/src/renderer/__tests__/e2e/Camera.spec.tsx +475 -0
- package/src/renderer/__tests__/e2e/LightingImageFilters.spec.tsx +1478 -0
- package/src/renderer/__tests__/e2e/Skottie.spec.tsx +440 -0
- package/src/renderer/__tests__/e2e/setup/skottie/basic_slots.json +1118 -0
- package/src/renderer/__tests__/e2e/setup/skottie/color-props.json +1 -0
- package/src/renderer/__tests__/e2e/setup/skottie/confetti.json +5899 -0
- package/src/renderer/__tests__/e2e/setup/skottie/drinks.json +43857 -0
- package/src/renderer/__tests__/e2e/setup/skottie/fingerprint.json +1 -0
- package/src/renderer/__tests__/e2e/setup/skottie/lego_loader.json +29540 -0
- package/src/renderer/__tests__/e2e/setup/skottie/new-drop.json +1 -0
- package/src/renderer/__tests__/e2e/setup/skottie/onboarding.json +1 -0
- package/src/renderer/__tests__/e2e/setup/skottie/text-layer.json +1 -0
- package/src/renderer/__tests__/setup.tsx +23 -0
- package/src/renderer/components/Skottie.tsx +8 -0
- package/src/renderer/components/index.ts +1 -0
- package/src/skia/__tests__/assets/Avenir-Heavy.ttf +0 -0
- package/src/skia/types/ImageFilter/ImageFilterFactory.ts +391 -21
- package/src/skia/types/Matrix4.ts +108 -2
- package/src/skia/types/Recorder.ts +2 -0
- package/src/skia/types/Skia.ts +2 -0
- package/src/skia/types/Skottie.ts +266 -0
- package/src/skia/types/index.ts +1 -0
- package/src/skia/web/JsiSkImageFilterFactory.ts +266 -31
- package/src/skia/web/JsiSkia.ts +2 -0
- package/src/skia/web/JsiSkottieAnimation.ts +259 -0
- package/src/skia/web/JsiSkottieFactory.ts +25 -0
- package/src/sksg/Elements.tsx +2 -0
- package/src/sksg/Recorder/Core.ts +3 -0
- package/src/sksg/Recorder/Player.ts +3 -0
- package/src/sksg/Recorder/ReanimatedRecorder.ts +6 -0
- package/src/sksg/Recorder/Recorder.ts +5 -0
- package/src/sksg/Recorder/Visitor.ts +3 -0
- package/src/sksg/Recorder/commands/Drawing.ts +7 -3
- package/src/sksg/Recorder/commands/ImageFilters.ts +1 -1
@@ -0,0 +1,980 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright 2018 Google Inc.
|
3
|
+
*
|
4
|
+
* Use of this source code is governed by a BSD-style license that can be
|
5
|
+
* found in the LICENSE file.
|
6
|
+
*/
|
7
|
+
|
8
|
+
#include "modules/jsonreader/SkJSONReader.h"
|
9
|
+
|
10
|
+
#include "include/core/SkData.h"
|
11
|
+
#include "include/core/SkRefCnt.h"
|
12
|
+
#include "include/core/SkStream.h"
|
13
|
+
#include "include/core/SkString.h"
|
14
|
+
#include "include/private/base/SkDebug.h"
|
15
|
+
#include "include/private/base/SkMalloc.h"
|
16
|
+
#include "include/private/base/SkTo.h"
|
17
|
+
#include "include/utils/SkParse.h"
|
18
|
+
#include "src/base/SkArenaAlloc.h"
|
19
|
+
#include "src/base/SkUTF.h"
|
20
|
+
|
21
|
+
#include <cmath>
|
22
|
+
#include <cstdint>
|
23
|
+
#include <cstdlib>
|
24
|
+
#include <limits>
|
25
|
+
#include <new>
|
26
|
+
#include <tuple>
|
27
|
+
#include <vector>
|
28
|
+
|
29
|
+
namespace skjson {
|
30
|
+
|
31
|
+
// #define SK_JSON_REPORT_ERRORS
|
32
|
+
|
33
|
+
static_assert( sizeof(Value) == 8, "");
|
34
|
+
static_assert(alignof(Value) == 8, "");
|
35
|
+
|
36
|
+
static constexpr size_t kRecAlign = alignof(Value);
|
37
|
+
|
38
|
+
void Value::init_tagged(Tag t) {
|
39
|
+
memset(fData8, 0, sizeof(fData8));
|
40
|
+
fData8[0] = SkTo<uint8_t>(t);
|
41
|
+
SkASSERT(this->getTag() == t);
|
42
|
+
}
|
43
|
+
|
44
|
+
// Pointer values store a type (in the lower kTagBits bits) and a pointer.
|
45
|
+
void Value::init_tagged_pointer(Tag t, void* p) {
|
46
|
+
if (sizeof(Value) == sizeof(uintptr_t)) {
|
47
|
+
*this->cast<uintptr_t>() = reinterpret_cast<uintptr_t>(p);
|
48
|
+
// For 64-bit, we rely on the pointer lower bits being zero.
|
49
|
+
SkASSERT(!(fData8[0] & kTagMask));
|
50
|
+
fData8[0] |= SkTo<uint8_t>(t);
|
51
|
+
} else {
|
52
|
+
// For 32-bit, we store the pointer in the upper word
|
53
|
+
SkASSERT(sizeof(Value) == sizeof(uintptr_t) * 2);
|
54
|
+
this->init_tagged(t);
|
55
|
+
*this->cast<uintptr_t>() = reinterpret_cast<uintptr_t>(p);
|
56
|
+
}
|
57
|
+
|
58
|
+
SkASSERT(this->getTag() == t);
|
59
|
+
SkASSERT(this->ptr<void>() == p);
|
60
|
+
}
|
61
|
+
|
62
|
+
NullValue::NullValue() {
|
63
|
+
this->init_tagged(Tag::kNull);
|
64
|
+
SkASSERT(this->getTag() == Tag::kNull);
|
65
|
+
}
|
66
|
+
|
67
|
+
BoolValue::BoolValue(bool b) {
|
68
|
+
this->init_tagged(Tag::kBool);
|
69
|
+
*this->cast<bool>() = b;
|
70
|
+
SkASSERT(this->getTag() == Tag::kBool);
|
71
|
+
}
|
72
|
+
|
73
|
+
NumberValue::NumberValue(int32_t i) {
|
74
|
+
this->init_tagged(Tag::kInt);
|
75
|
+
*this->cast<int32_t>() = i;
|
76
|
+
SkASSERT(this->getTag() == Tag::kInt);
|
77
|
+
}
|
78
|
+
|
79
|
+
NumberValue::NumberValue(float f) {
|
80
|
+
this->init_tagged(Tag::kFloat);
|
81
|
+
*this->cast<float>() = f;
|
82
|
+
SkASSERT(this->getTag() == Tag::kFloat);
|
83
|
+
}
|
84
|
+
|
85
|
+
// Vector recs point to externally allocated slabs with the following layout:
|
86
|
+
//
|
87
|
+
// [size_t n] [REC_0] ... [REC_n-1] [optional extra trailing storage]
|
88
|
+
//
|
89
|
+
// Long strings use extra_alloc_size == 1 to store the \0 terminator.
|
90
|
+
//
|
91
|
+
template <typename T, size_t extra_alloc_size = 0>
|
92
|
+
static void* MakeVector(size_t vec_size, const void* src, size_t src_size, SkArenaAlloc& alloc) {
|
93
|
+
// The Ts are already in memory, so their size should be safe.
|
94
|
+
const auto total_size = sizeof(size_t) + vec_size * sizeof(T) + extra_alloc_size;
|
95
|
+
auto* size_ptr = reinterpret_cast<size_t*>(alloc.makeBytesAlignedTo(total_size, kRecAlign));
|
96
|
+
|
97
|
+
*size_ptr = vec_size;
|
98
|
+
sk_careful_memcpy(size_ptr + 1, src, src_size * sizeof(T));
|
99
|
+
|
100
|
+
return size_ptr;
|
101
|
+
}
|
102
|
+
|
103
|
+
template <typename T, size_t extra_alloc_size = 0>
|
104
|
+
static void* MakeVector(size_t vec_size, const void* src, SkArenaAlloc& alloc) {
|
105
|
+
return MakeVector<T, extra_alloc_size>(vec_size, src, vec_size, alloc);
|
106
|
+
}
|
107
|
+
|
108
|
+
ArrayValue::ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc) {
|
109
|
+
this->init_tagged_pointer(Tag::kArray, MakeVector<Value>(size, src, alloc));
|
110
|
+
SkASSERT(this->getTag() == Tag::kArray);
|
111
|
+
}
|
112
|
+
|
113
|
+
// Strings have two flavors:
|
114
|
+
//
|
115
|
+
// -- short strings (len <= 7) -> these are stored inline, in the record
|
116
|
+
// (one byte reserved for null terminator/type):
|
117
|
+
//
|
118
|
+
// [str] [\0]|[max_len - actual_len]
|
119
|
+
//
|
120
|
+
// Storing [max_len - actual_len] allows the 'len' field to double-up as a
|
121
|
+
// null terminator when size == max_len (this works 'cause kShortString == 0).
|
122
|
+
//
|
123
|
+
// -- long strings (len > 7) -> these are externally allocated vectors (VectorRec<char>).
|
124
|
+
//
|
125
|
+
// The string data plus a null-char terminator are copied over.
|
126
|
+
//
|
127
|
+
namespace {
|
128
|
+
|
129
|
+
// An internal string builder with a fast 8 byte short string load path
|
130
|
+
// (for the common case where the string is not at the end of the stream).
|
131
|
+
class FastString final : public Value {
|
132
|
+
public:
|
133
|
+
FastString(const char* src, size_t size, const char* eos, SkArenaAlloc& alloc) {
|
134
|
+
SkASSERT(src <= eos);
|
135
|
+
|
136
|
+
if (size > kMaxInlineStringSize) {
|
137
|
+
this->initLongString(src, size, alloc);
|
138
|
+
SkASSERT(this->getTag() == Tag::kString);
|
139
|
+
return;
|
140
|
+
}
|
141
|
+
|
142
|
+
// initFastShortString is faster (doh), but requires access to 6 chars past src.
|
143
|
+
if (src && src + 6 <= eos) {
|
144
|
+
this->initFastShortString(src, size);
|
145
|
+
} else {
|
146
|
+
this->initShortString(src, size);
|
147
|
+
}
|
148
|
+
|
149
|
+
SkASSERT(this->getTag() == Tag::kShortString);
|
150
|
+
}
|
151
|
+
|
152
|
+
private:
|
153
|
+
// first byte reserved for tagging, \0 terminator => 6 usable chars
|
154
|
+
inline static constexpr size_t kMaxInlineStringSize = sizeof(Value) - 2;
|
155
|
+
|
156
|
+
void initLongString(const char* src, size_t size, SkArenaAlloc& alloc) {
|
157
|
+
SkASSERT(size > kMaxInlineStringSize);
|
158
|
+
|
159
|
+
this->init_tagged_pointer(Tag::kString, MakeVector<char, 1>(size, src, alloc));
|
160
|
+
|
161
|
+
auto* data = this->cast<VectorValue<char, Value::Type::kString>>()->begin();
|
162
|
+
const_cast<char*>(data)[size] = '\0';
|
163
|
+
}
|
164
|
+
|
165
|
+
void initShortString(const char* src, size_t size) {
|
166
|
+
SkASSERT(size <= kMaxInlineStringSize);
|
167
|
+
|
168
|
+
this->init_tagged(Tag::kShortString);
|
169
|
+
sk_careful_memcpy(this->cast<char>(), src, size);
|
170
|
+
// Null terminator provided by init_tagged() above (fData8 is zero-initialized).
|
171
|
+
}
|
172
|
+
|
173
|
+
void initFastShortString(const char* src, size_t size) {
|
174
|
+
SkASSERT(size <= kMaxInlineStringSize);
|
175
|
+
|
176
|
+
uint64_t* s64 = this->cast<uint64_t>();
|
177
|
+
|
178
|
+
// Load 8 chars and mask out the tag and \0 terminator.
|
179
|
+
// Note: we picked kShortString == 0 to avoid setting explicitly below.
|
180
|
+
static_assert(SkToU8(Tag::kShortString) == 0, "please don't break this");
|
181
|
+
|
182
|
+
// Since the first byte is occupied by the tag, we want the string chars [0..5] to land
|
183
|
+
// on bytes [1..6] => the fastest way is to read8 @(src - 1) (always safe, because the
|
184
|
+
// string requires a " prefix at the very least).
|
185
|
+
memcpy(s64, src - 1, 8);
|
186
|
+
|
187
|
+
#if defined(SK_CPU_LENDIAN)
|
188
|
+
// The mask for a max-length string (6), with a leading tag and trailing \0 is
|
189
|
+
// 0x00ffffffffffff00. Accounting for the final left-shift, this becomes
|
190
|
+
// 0x0000ffffffffffff.
|
191
|
+
*s64 &= (0x0000ffffffffffffULL >> ((kMaxInlineStringSize - size) * 8)) // trailing \0s
|
192
|
+
<< 8; // tag byte
|
193
|
+
#else
|
194
|
+
static_assert(false, "Big-endian builds are not supported at this time.");
|
195
|
+
#endif
|
196
|
+
}
|
197
|
+
};
|
198
|
+
|
199
|
+
} // namespace
|
200
|
+
|
201
|
+
StringValue::StringValue(const char* src, SkArenaAlloc& alloc)
|
202
|
+
: StringValue(src, strlen(src), alloc) {}
|
203
|
+
|
204
|
+
StringValue::StringValue(const char* src, size_t size, SkArenaAlloc& alloc) {
|
205
|
+
new (this) FastString(src, size, src, alloc);
|
206
|
+
}
|
207
|
+
|
208
|
+
ObjectValue::ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc) {
|
209
|
+
this->init_tagged_pointer(Tag::kObject, MakeVector<Member>(size, src, alloc));
|
210
|
+
SkASSERT(this->getTag() == Tag::kObject);
|
211
|
+
}
|
212
|
+
|
213
|
+
// Boring public Value glue.
|
214
|
+
|
215
|
+
static int inline_strcmp(const char a[], const char b[]) {
|
216
|
+
for (;;) {
|
217
|
+
char c = *a++;
|
218
|
+
if (c == 0) {
|
219
|
+
break;
|
220
|
+
}
|
221
|
+
if (c != *b++) {
|
222
|
+
return 1;
|
223
|
+
}
|
224
|
+
}
|
225
|
+
return *b != 0;
|
226
|
+
}
|
227
|
+
|
228
|
+
const Member* ObjectValue::find(const char* key) const {
|
229
|
+
// Reverse search for duplicates resolution (policy: return last).
|
230
|
+
const auto* begin = this->begin();
|
231
|
+
const auto* member = this->end();
|
232
|
+
|
233
|
+
while (member > begin) {
|
234
|
+
--member;
|
235
|
+
if (0 == inline_strcmp(key, member->fKey.as<StringValue>().begin())) {
|
236
|
+
return member;
|
237
|
+
}
|
238
|
+
}
|
239
|
+
|
240
|
+
return nullptr;
|
241
|
+
}
|
242
|
+
|
243
|
+
Value& ObjectValue::writable(const char* key, SkArenaAlloc& alloc) const {
|
244
|
+
Member* writable_member = const_cast<Member*>(this->find(key));
|
245
|
+
|
246
|
+
if (!writable_member) {
|
247
|
+
ObjectValue* writable_obj = const_cast<ObjectValue*>(this);
|
248
|
+
writable_obj->init_tagged_pointer(Tag::kObject, MakeVector<Member>(this->size() + 1,
|
249
|
+
this->begin(),
|
250
|
+
this->size(),
|
251
|
+
alloc));
|
252
|
+
writable_member = const_cast<Member*>(writable_obj->end() - 1);
|
253
|
+
writable_member->fKey = StringValue(key, strlen(key), alloc);
|
254
|
+
writable_member->fValue = NullValue();
|
255
|
+
}
|
256
|
+
|
257
|
+
return writable_member->fValue;
|
258
|
+
}
|
259
|
+
|
260
|
+
namespace {
|
261
|
+
|
262
|
+
// Lexer/parser inspired by rapidjson [1], sajson [2] and pjson [3].
|
263
|
+
//
|
264
|
+
// [1] https://github.com/Tencent/rapidjson/
|
265
|
+
// [2] https://github.com/chadaustin/sajson
|
266
|
+
// [3] https://pastebin.com/hnhSTL3h
|
267
|
+
|
268
|
+
// bit 0 (0x01) - plain ASCII string character
|
269
|
+
// bit 1 (0x02) - whitespace
|
270
|
+
// bit 2 (0x04) - string terminator (" \\ \0 [control chars] **AND } ]** <- see matchString notes)
|
271
|
+
// bit 3 (0x08) - 0-9
|
272
|
+
// bit 4 (0x10) - 0-9 e E .
|
273
|
+
// bit 5 (0x20) - scope terminator (} ])
|
274
|
+
static constexpr uint8_t g_token_flags[256] = {
|
275
|
+
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
276
|
+
4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 4, 4, 6, 4, 4, // 0
|
277
|
+
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 1
|
278
|
+
3, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x11,1, // 2
|
279
|
+
0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19, 1, 1, 1, 1, 1, 1, // 3
|
280
|
+
1, 1, 1, 1, 1, 0x11,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
|
281
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4,0x25, 1, 1, // 5
|
282
|
+
1, 1, 1, 1, 1, 0x11,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
|
283
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,0x25, 1, 1, // 7
|
284
|
+
|
285
|
+
// 128-255
|
286
|
+
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
|
287
|
+
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
|
288
|
+
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
|
289
|
+
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0
|
290
|
+
};
|
291
|
+
|
292
|
+
static inline bool is_ws(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x02; }
|
293
|
+
static inline bool is_eostring(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x04; }
|
294
|
+
static inline bool is_digit(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x08; }
|
295
|
+
static inline bool is_numeric(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x10; }
|
296
|
+
static inline bool is_eoscope(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x20; }
|
297
|
+
|
298
|
+
static inline const char* skip_ws(const char* p) {
|
299
|
+
while (is_ws(*p)) ++p;
|
300
|
+
return p;
|
301
|
+
}
|
302
|
+
|
303
|
+
static inline float pow10(int32_t exp) {
|
304
|
+
static constexpr float g_pow10_table[63] =
|
305
|
+
{
|
306
|
+
1.e-031f, 1.e-030f, 1.e-029f, 1.e-028f, 1.e-027f, 1.e-026f, 1.e-025f, 1.e-024f,
|
307
|
+
1.e-023f, 1.e-022f, 1.e-021f, 1.e-020f, 1.e-019f, 1.e-018f, 1.e-017f, 1.e-016f,
|
308
|
+
1.e-015f, 1.e-014f, 1.e-013f, 1.e-012f, 1.e-011f, 1.e-010f, 1.e-009f, 1.e-008f,
|
309
|
+
1.e-007f, 1.e-006f, 1.e-005f, 1.e-004f, 1.e-003f, 1.e-002f, 1.e-001f, 1.e+000f,
|
310
|
+
1.e+001f, 1.e+002f, 1.e+003f, 1.e+004f, 1.e+005f, 1.e+006f, 1.e+007f, 1.e+008f,
|
311
|
+
1.e+009f, 1.e+010f, 1.e+011f, 1.e+012f, 1.e+013f, 1.e+014f, 1.e+015f, 1.e+016f,
|
312
|
+
1.e+017f, 1.e+018f, 1.e+019f, 1.e+020f, 1.e+021f, 1.e+022f, 1.e+023f, 1.e+024f,
|
313
|
+
1.e+025f, 1.e+026f, 1.e+027f, 1.e+028f, 1.e+029f, 1.e+030f, 1.e+031f
|
314
|
+
};
|
315
|
+
|
316
|
+
static constexpr int32_t k_exp_offset = std::size(g_pow10_table) / 2;
|
317
|
+
|
318
|
+
// We only support negative exponents for now.
|
319
|
+
SkASSERT(exp <= 0);
|
320
|
+
|
321
|
+
return (exp >= -k_exp_offset) ? g_pow10_table[exp + k_exp_offset]
|
322
|
+
: std::pow(10.0f, static_cast<float>(exp));
|
323
|
+
}
|
324
|
+
|
325
|
+
class DOMParser {
|
326
|
+
public:
|
327
|
+
explicit DOMParser(SkArenaAlloc& alloc) : fAlloc(alloc) {
|
328
|
+
fValueStack.reserve(kValueStackReserve);
|
329
|
+
fUnescapeBuffer.reserve(kUnescapeBufferReserve);
|
330
|
+
}
|
331
|
+
|
332
|
+
Value parse(const char* p, size_t size) {
|
333
|
+
if (!size) {
|
334
|
+
return this->error(NullValue(), p, "invalid empty input");
|
335
|
+
}
|
336
|
+
|
337
|
+
const char* p_stop = p + size - 1;
|
338
|
+
|
339
|
+
// We're only checking for end-of-stream on object/array close('}',']'),
|
340
|
+
// so we must trim any whitespace from the buffer tail.
|
341
|
+
while (p_stop > p && is_ws(*p_stop)) --p_stop;
|
342
|
+
|
343
|
+
SkASSERT(p_stop >= p && p_stop < p + size);
|
344
|
+
if (!is_eoscope(*p_stop)) {
|
345
|
+
return this->error(NullValue(), p_stop, "invalid top-level value");
|
346
|
+
}
|
347
|
+
|
348
|
+
p = skip_ws(p);
|
349
|
+
|
350
|
+
switch (*p) {
|
351
|
+
case '{':
|
352
|
+
goto match_object;
|
353
|
+
case '[':
|
354
|
+
goto match_array;
|
355
|
+
default:
|
356
|
+
return this->error(NullValue(), p, "invalid top-level value");
|
357
|
+
}
|
358
|
+
|
359
|
+
match_object:
|
360
|
+
SkASSERT(*p == '{');
|
361
|
+
p = skip_ws(p + 1);
|
362
|
+
|
363
|
+
this->pushObjectScope();
|
364
|
+
|
365
|
+
if (*p == '}') goto pop_object;
|
366
|
+
|
367
|
+
// goto match_object_key;
|
368
|
+
match_object_key:
|
369
|
+
p = skip_ws(p);
|
370
|
+
if (*p != '"') return this->error(NullValue(), p, "expected object key");
|
371
|
+
|
372
|
+
p = this->matchString(p, p_stop, [this](const char* key, size_t size, const char* eos) {
|
373
|
+
this->pushObjectKey(key, size, eos);
|
374
|
+
});
|
375
|
+
if (!p) return NullValue();
|
376
|
+
|
377
|
+
p = skip_ws(p);
|
378
|
+
if (*p != ':') return this->error(NullValue(), p, "expected ':' separator");
|
379
|
+
|
380
|
+
++p;
|
381
|
+
|
382
|
+
// goto match_value;
|
383
|
+
match_value:
|
384
|
+
p = skip_ws(p);
|
385
|
+
|
386
|
+
switch (*p) {
|
387
|
+
case '\0':
|
388
|
+
return this->error(NullValue(), p, "unexpected input end");
|
389
|
+
case '"':
|
390
|
+
p = this->matchString(
|
391
|
+
p, p_stop, [this](const char* str, size_t size, const char* eos) {
|
392
|
+
this->pushString(str, size, eos);
|
393
|
+
});
|
394
|
+
break;
|
395
|
+
case '[':
|
396
|
+
goto match_array;
|
397
|
+
case 'f':
|
398
|
+
p = this->matchFalse(p);
|
399
|
+
break;
|
400
|
+
case 'n':
|
401
|
+
p = this->matchNull(p);
|
402
|
+
break;
|
403
|
+
case 't':
|
404
|
+
p = this->matchTrue(p);
|
405
|
+
break;
|
406
|
+
case '{':
|
407
|
+
goto match_object;
|
408
|
+
default:
|
409
|
+
p = this->matchNumber(p);
|
410
|
+
break;
|
411
|
+
}
|
412
|
+
|
413
|
+
if (!p) return NullValue();
|
414
|
+
|
415
|
+
// goto match_post_value;
|
416
|
+
match_post_value:
|
417
|
+
SkASSERT(!this->inTopLevelScope());
|
418
|
+
|
419
|
+
p = skip_ws(p);
|
420
|
+
switch (*p) {
|
421
|
+
case ',':
|
422
|
+
++p;
|
423
|
+
if (this->inObjectScope()) {
|
424
|
+
goto match_object_key;
|
425
|
+
} else {
|
426
|
+
SkASSERT(this->inArrayScope());
|
427
|
+
goto match_value;
|
428
|
+
}
|
429
|
+
case ']':
|
430
|
+
goto pop_array;
|
431
|
+
case '}':
|
432
|
+
goto pop_object;
|
433
|
+
default:
|
434
|
+
return this->error(NullValue(), p - 1, "unexpected value-trailing token");
|
435
|
+
}
|
436
|
+
|
437
|
+
// unreachable
|
438
|
+
SkASSERT(false);
|
439
|
+
|
440
|
+
pop_object:
|
441
|
+
SkASSERT(*p == '}');
|
442
|
+
|
443
|
+
if (this->inArrayScope()) {
|
444
|
+
return this->error(NullValue(), p, "unexpected object terminator");
|
445
|
+
}
|
446
|
+
|
447
|
+
this->popObjectScope();
|
448
|
+
|
449
|
+
// goto pop_common
|
450
|
+
pop_common:
|
451
|
+
SkASSERT(is_eoscope(*p));
|
452
|
+
|
453
|
+
if (this->inTopLevelScope()) {
|
454
|
+
SkASSERT(fValueStack.size() == 1);
|
455
|
+
|
456
|
+
// Success condition: parsed the top level element and reached the stop token.
|
457
|
+
return p == p_stop ? fValueStack.front()
|
458
|
+
: this->error(NullValue(), p + 1, "trailing root garbage");
|
459
|
+
}
|
460
|
+
|
461
|
+
if (p == p_stop) {
|
462
|
+
return this->error(NullValue(), p, "unexpected end-of-input");
|
463
|
+
}
|
464
|
+
|
465
|
+
++p;
|
466
|
+
|
467
|
+
goto match_post_value;
|
468
|
+
|
469
|
+
match_array:
|
470
|
+
SkASSERT(*p == '[');
|
471
|
+
p = skip_ws(p + 1);
|
472
|
+
|
473
|
+
this->pushArrayScope();
|
474
|
+
|
475
|
+
if (*p != ']') goto match_value;
|
476
|
+
|
477
|
+
// goto pop_array;
|
478
|
+
pop_array:
|
479
|
+
SkASSERT(*p == ']');
|
480
|
+
|
481
|
+
if (this->inObjectScope()) {
|
482
|
+
return this->error(NullValue(), p, "unexpected array terminator");
|
483
|
+
}
|
484
|
+
|
485
|
+
this->popArrayScope();
|
486
|
+
|
487
|
+
goto pop_common;
|
488
|
+
|
489
|
+
SkASSERT(false);
|
490
|
+
return NullValue();
|
491
|
+
}
|
492
|
+
|
493
|
+
std::tuple<const char*, const SkString> getError() const {
|
494
|
+
return std::make_tuple(fErrorToken, fErrorMessage);
|
495
|
+
}
|
496
|
+
|
497
|
+
private:
|
498
|
+
SkArenaAlloc& fAlloc;
|
499
|
+
|
500
|
+
// Pending values stack.
|
501
|
+
inline static constexpr size_t kValueStackReserve = 256;
|
502
|
+
std::vector<Value> fValueStack;
|
503
|
+
|
504
|
+
// String unescape buffer.
|
505
|
+
inline static constexpr size_t kUnescapeBufferReserve = 512;
|
506
|
+
std::vector<char> fUnescapeBuffer;
|
507
|
+
|
508
|
+
// Tracks the current object/array scope, as an index into fStack:
|
509
|
+
//
|
510
|
+
// - for objects: fScopeIndex = (index of first value in scope)
|
511
|
+
// - for arrays : fScopeIndex = -(index of first value in scope)
|
512
|
+
//
|
513
|
+
// fScopeIndex == 0 IFF we are at the top level (no current/active scope).
|
514
|
+
intptr_t fScopeIndex = 0;
|
515
|
+
|
516
|
+
// Error reporting.
|
517
|
+
const char* fErrorToken = nullptr;
|
518
|
+
SkString fErrorMessage;
|
519
|
+
|
520
|
+
bool inTopLevelScope() const { return fScopeIndex == 0; }
|
521
|
+
bool inObjectScope() const { return fScopeIndex > 0; }
|
522
|
+
bool inArrayScope() const { return fScopeIndex < 0; }
|
523
|
+
|
524
|
+
// Helper for masquerading raw primitive types as Values (bypassing tagging, etc).
|
525
|
+
template <typename T>
|
526
|
+
class RawValue final : public Value {
|
527
|
+
public:
|
528
|
+
explicit RawValue(T v) {
|
529
|
+
static_assert(sizeof(T) <= sizeof(Value), "");
|
530
|
+
*this->cast<T>() = v;
|
531
|
+
}
|
532
|
+
|
533
|
+
T operator*() const { return *this->cast<T>(); }
|
534
|
+
};
|
535
|
+
|
536
|
+
template <typename VectorT>
|
537
|
+
void popScopeAsVec(size_t scope_start) {
|
538
|
+
SkASSERT(scope_start > 0);
|
539
|
+
SkASSERT(scope_start <= fValueStack.size());
|
540
|
+
|
541
|
+
using T = typename VectorT::ValueT;
|
542
|
+
static_assert( sizeof(T) >= sizeof(Value), "");
|
543
|
+
static_assert( sizeof(T) % sizeof(Value) == 0, "");
|
544
|
+
static_assert(alignof(T) == alignof(Value), "");
|
545
|
+
|
546
|
+
const auto scope_count = fValueStack.size() - scope_start,
|
547
|
+
count = scope_count / (sizeof(T) / sizeof(Value));
|
548
|
+
SkASSERT(scope_count % (sizeof(T) / sizeof(Value)) == 0);
|
549
|
+
|
550
|
+
const auto* begin = reinterpret_cast<const T*>(fValueStack.data() + scope_start);
|
551
|
+
|
552
|
+
// Restore the previous scope index from saved placeholder value,
|
553
|
+
// and instantiate as a vector of values in scope.
|
554
|
+
auto& placeholder = fValueStack[scope_start - 1];
|
555
|
+
fScopeIndex = *static_cast<RawValue<intptr_t>&>(placeholder);
|
556
|
+
placeholder = VectorT(begin, count, fAlloc);
|
557
|
+
|
558
|
+
// Drop the (consumed) values in scope.
|
559
|
+
fValueStack.resize(scope_start);
|
560
|
+
}
|
561
|
+
|
562
|
+
void pushObjectScope() {
|
563
|
+
// Save a scope index now, and then later we'll overwrite this value as the Object itself.
|
564
|
+
fValueStack.push_back(RawValue<intptr_t>(fScopeIndex));
|
565
|
+
|
566
|
+
// New object scope.
|
567
|
+
fScopeIndex = SkTo<intptr_t>(fValueStack.size());
|
568
|
+
}
|
569
|
+
|
570
|
+
void popObjectScope() {
|
571
|
+
SkASSERT(this->inObjectScope());
|
572
|
+
this->popScopeAsVec<ObjectValue>(SkTo<size_t>(fScopeIndex));
|
573
|
+
|
574
|
+
SkDEBUGCODE(
|
575
|
+
const auto& obj = fValueStack.back().as<ObjectValue>();
|
576
|
+
SkASSERT(obj.is<ObjectValue>());
|
577
|
+
for (const auto& member : obj) {
|
578
|
+
SkASSERT(member.fKey.is<StringValue>());
|
579
|
+
}
|
580
|
+
)
|
581
|
+
}
|
582
|
+
|
583
|
+
void pushArrayScope() {
|
584
|
+
// Save a scope index now, and then later we'll overwrite this value as the Array itself.
|
585
|
+
fValueStack.push_back(RawValue<intptr_t>(fScopeIndex));
|
586
|
+
|
587
|
+
// New array scope.
|
588
|
+
fScopeIndex = -SkTo<intptr_t>(fValueStack.size());
|
589
|
+
}
|
590
|
+
|
591
|
+
void popArrayScope() {
|
592
|
+
SkASSERT(this->inArrayScope());
|
593
|
+
this->popScopeAsVec<ArrayValue>(SkTo<size_t>(-fScopeIndex));
|
594
|
+
|
595
|
+
SkDEBUGCODE(
|
596
|
+
const auto& arr = fValueStack.back().as<ArrayValue>();
|
597
|
+
SkASSERT(arr.is<ArrayValue>());
|
598
|
+
)
|
599
|
+
}
|
600
|
+
|
601
|
+
void pushObjectKey(const char* key, size_t size, const char* eos) {
|
602
|
+
SkASSERT(this->inObjectScope());
|
603
|
+
SkASSERT(fValueStack.size() >= SkTo<size_t>(fScopeIndex));
|
604
|
+
SkASSERT(!((fValueStack.size() - SkTo<size_t>(fScopeIndex)) & 1));
|
605
|
+
this->pushString(key, size, eos);
|
606
|
+
}
|
607
|
+
|
608
|
+
void pushTrue() { fValueStack.push_back(BoolValue(true)); }
|
609
|
+
|
610
|
+
void pushFalse() { fValueStack.push_back(BoolValue(false)); }
|
611
|
+
|
612
|
+
void pushNull() { fValueStack.push_back(NullValue()); }
|
613
|
+
|
614
|
+
void pushString(const char* s, size_t size, const char* eos) {
|
615
|
+
fValueStack.push_back(FastString(s, size, eos, fAlloc));
|
616
|
+
}
|
617
|
+
|
618
|
+
void pushInt32(int32_t i) { fValueStack.push_back(NumberValue(i)); }
|
619
|
+
|
620
|
+
void pushFloat(float f) { fValueStack.push_back(NumberValue(f)); }
|
621
|
+
|
622
|
+
template <typename T>
|
623
|
+
T error(T&& ret_val, const char* p, const char* msg) {
|
624
|
+
#if defined(SK_JSON_REPORT_ERRORS)
|
625
|
+
fErrorToken = p;
|
626
|
+
fErrorMessage.set(msg);
|
627
|
+
#endif
|
628
|
+
return ret_val;
|
629
|
+
}
|
630
|
+
|
631
|
+
const char* matchTrue(const char* p) {
|
632
|
+
SkASSERT(p[0] == 't');
|
633
|
+
|
634
|
+
if (p[1] == 'r' && p[2] == 'u' && p[3] == 'e') {
|
635
|
+
this->pushTrue();
|
636
|
+
return p + 4;
|
637
|
+
}
|
638
|
+
|
639
|
+
return this->error(nullptr, p, "invalid token");
|
640
|
+
}
|
641
|
+
|
642
|
+
const char* matchFalse(const char* p) {
|
643
|
+
SkASSERT(p[0] == 'f');
|
644
|
+
|
645
|
+
if (p[1] == 'a' && p[2] == 'l' && p[3] == 's' && p[4] == 'e') {
|
646
|
+
this->pushFalse();
|
647
|
+
return p + 5;
|
648
|
+
}
|
649
|
+
|
650
|
+
return this->error(nullptr, p, "invalid token");
|
651
|
+
}
|
652
|
+
|
653
|
+
const char* matchNull(const char* p) {
|
654
|
+
SkASSERT(p[0] == 'n');
|
655
|
+
|
656
|
+
if (p[1] == 'u' && p[2] == 'l' && p[3] == 'l') {
|
657
|
+
this->pushNull();
|
658
|
+
return p + 4;
|
659
|
+
}
|
660
|
+
|
661
|
+
return this->error(nullptr, p, "invalid token");
|
662
|
+
}
|
663
|
+
|
664
|
+
const std::vector<char>* unescapeString(const char* begin, const char* end) {
|
665
|
+
fUnescapeBuffer.clear();
|
666
|
+
|
667
|
+
for (const auto* p = begin; p != end; ++p) {
|
668
|
+
if (*p != '\\') {
|
669
|
+
fUnescapeBuffer.push_back(*p);
|
670
|
+
continue;
|
671
|
+
}
|
672
|
+
|
673
|
+
if (++p == end) {
|
674
|
+
return nullptr;
|
675
|
+
}
|
676
|
+
|
677
|
+
switch (*p) {
|
678
|
+
case '"': fUnescapeBuffer.push_back( '"'); break;
|
679
|
+
case '\\': fUnescapeBuffer.push_back('\\'); break;
|
680
|
+
case '/': fUnescapeBuffer.push_back( '/'); break;
|
681
|
+
case 'b': fUnescapeBuffer.push_back('\b'); break;
|
682
|
+
case 'f': fUnescapeBuffer.push_back('\f'); break;
|
683
|
+
case 'n': fUnescapeBuffer.push_back('\n'); break;
|
684
|
+
case 'r': fUnescapeBuffer.push_back('\r'); break;
|
685
|
+
case 't': fUnescapeBuffer.push_back('\t'); break;
|
686
|
+
case 'u': {
|
687
|
+
if (p + 4 >= end) {
|
688
|
+
return nullptr;
|
689
|
+
}
|
690
|
+
|
691
|
+
uint32_t hexed;
|
692
|
+
const char hex_str[] = {p[1], p[2], p[3], p[4], '\0'};
|
693
|
+
const auto* eos = SkParse::FindHex(hex_str, &hexed);
|
694
|
+
if (!eos || *eos) {
|
695
|
+
return nullptr;
|
696
|
+
}
|
697
|
+
|
698
|
+
char utf8[SkUTF::kMaxBytesInUTF8Sequence];
|
699
|
+
const auto utf8_len = SkUTF::ToUTF8(SkTo<SkUnichar>(hexed), utf8);
|
700
|
+
fUnescapeBuffer.insert(fUnescapeBuffer.end(), utf8, utf8 + utf8_len);
|
701
|
+
p += 4;
|
702
|
+
} break;
|
703
|
+
default:
|
704
|
+
return nullptr;
|
705
|
+
}
|
706
|
+
}
|
707
|
+
|
708
|
+
return &fUnescapeBuffer;
|
709
|
+
}
|
710
|
+
|
711
|
+
template <typename MatchFunc>
|
712
|
+
const char* matchString(const char* p, const char* p_stop, MatchFunc&& func) {
|
713
|
+
SkASSERT(*p == '"');
|
714
|
+
const auto* s_begin = p + 1;
|
715
|
+
bool requires_unescape = false;
|
716
|
+
|
717
|
+
do {
|
718
|
+
// Consume string chars.
|
719
|
+
// This is the fast path, and hopefully we only hit it once then quick-exit below.
|
720
|
+
for (p = p + 1; !is_eostring(*p); ++p);
|
721
|
+
|
722
|
+
if (*p == '"') {
|
723
|
+
// Valid string found.
|
724
|
+
if (!requires_unescape) {
|
725
|
+
func(s_begin, p - s_begin, p_stop);
|
726
|
+
} else {
|
727
|
+
// Slow unescape. We could avoid this extra copy with some effort,
|
728
|
+
// but in practice escaped strings should be rare.
|
729
|
+
const auto* buf = this->unescapeString(s_begin, p);
|
730
|
+
if (!buf) {
|
731
|
+
break;
|
732
|
+
}
|
733
|
+
|
734
|
+
SkASSERT(!buf->empty());
|
735
|
+
func(buf->data(), buf->size(), buf->data() + buf->size() - 1);
|
736
|
+
}
|
737
|
+
return p + 1;
|
738
|
+
}
|
739
|
+
|
740
|
+
if (*p == '\\') {
|
741
|
+
requires_unescape = true;
|
742
|
+
++p;
|
743
|
+
continue;
|
744
|
+
}
|
745
|
+
|
746
|
+
// End-of-scope chars are special: we use them to tag the end of the input.
|
747
|
+
// Thus they cannot be consumed indiscriminately -- we need to check if we hit the
|
748
|
+
// end of the input. To that effect, we treat them as string terminators above,
|
749
|
+
// then we catch them here.
|
750
|
+
if (is_eoscope(*p)) {
|
751
|
+
continue;
|
752
|
+
}
|
753
|
+
|
754
|
+
// Invalid/unexpected char.
|
755
|
+
break;
|
756
|
+
} while (p != p_stop);
|
757
|
+
|
758
|
+
// Premature end-of-input, or illegal string char.
|
759
|
+
return this->error(nullptr, s_begin - 1, "invalid string");
|
760
|
+
}
|
761
|
+
|
762
|
+
const char* matchFastFloatDecimalPart(const char* p, int sign, float f, int exp) {
|
763
|
+
SkASSERT(exp <= 0);
|
764
|
+
|
765
|
+
for (;;) {
|
766
|
+
if (!is_digit(*p)) break;
|
767
|
+
f = f * 10.f + (*p++ - '0'); --exp;
|
768
|
+
if (!is_digit(*p)) break;
|
769
|
+
f = f * 10.f + (*p++ - '0'); --exp;
|
770
|
+
}
|
771
|
+
|
772
|
+
const auto decimal_scale = pow10(exp);
|
773
|
+
if (is_numeric(*p) || !decimal_scale) {
|
774
|
+
SkASSERT((*p == '.' || *p == 'e' || *p == 'E') || !decimal_scale);
|
775
|
+
// Malformed input, or an (unsupported) exponent, or a collapsed decimal factor.
|
776
|
+
return nullptr;
|
777
|
+
}
|
778
|
+
|
779
|
+
this->pushFloat(sign * f * decimal_scale);
|
780
|
+
|
781
|
+
return p;
|
782
|
+
}
|
783
|
+
|
784
|
+
const char* matchFastFloatPart(const char* p, int sign, float f) {
|
785
|
+
for (;;) {
|
786
|
+
if (!is_digit(*p)) break;
|
787
|
+
f = f * 10.f + (*p++ - '0');
|
788
|
+
if (!is_digit(*p)) break;
|
789
|
+
f = f * 10.f + (*p++ - '0');
|
790
|
+
}
|
791
|
+
|
792
|
+
if (!is_numeric(*p)) {
|
793
|
+
// Matched (integral) float.
|
794
|
+
this->pushFloat(sign * f);
|
795
|
+
return p;
|
796
|
+
}
|
797
|
+
|
798
|
+
return (*p == '.') ? this->matchFastFloatDecimalPart(p + 1, sign, f, 0)
|
799
|
+
: nullptr;
|
800
|
+
}
|
801
|
+
|
802
|
+
const char* matchFast32OrFloat(const char* p) {
|
803
|
+
int sign = 1;
|
804
|
+
if (*p == '-') {
|
805
|
+
sign = -1;
|
806
|
+
++p;
|
807
|
+
}
|
808
|
+
|
809
|
+
const auto* digits_start = p;
|
810
|
+
|
811
|
+
int32_t n32 = 0;
|
812
|
+
|
813
|
+
// This is the largest absolute int32 value we can handle before
|
814
|
+
// risking overflow *on the next digit* (214748363).
|
815
|
+
static constexpr int32_t kMaxInt32 = (std::numeric_limits<int32_t>::max() - 9) / 10;
|
816
|
+
|
817
|
+
if (is_digit(*p)) {
|
818
|
+
n32 = (*p++ - '0');
|
819
|
+
for (;;) {
|
820
|
+
if (!is_digit(*p) || n32 > kMaxInt32) break;
|
821
|
+
n32 = n32 * 10 + (*p++ - '0');
|
822
|
+
}
|
823
|
+
}
|
824
|
+
|
825
|
+
if (!is_numeric(*p)) {
|
826
|
+
// Did we actually match any digits?
|
827
|
+
if (p > digits_start) {
|
828
|
+
this->pushInt32(sign * n32);
|
829
|
+
return p;
|
830
|
+
}
|
831
|
+
return nullptr;
|
832
|
+
}
|
833
|
+
|
834
|
+
if (*p == '.') {
|
835
|
+
const auto* decimals_start = ++p;
|
836
|
+
|
837
|
+
int exp = 0;
|
838
|
+
|
839
|
+
for (;;) {
|
840
|
+
if (!is_digit(*p) || n32 > kMaxInt32) break;
|
841
|
+
n32 = n32 * 10 + (*p++ - '0'); --exp;
|
842
|
+
if (!is_digit(*p) || n32 > kMaxInt32) break;
|
843
|
+
n32 = n32 * 10 + (*p++ - '0'); --exp;
|
844
|
+
}
|
845
|
+
|
846
|
+
if (!is_numeric(*p)) {
|
847
|
+
// Did we actually match any digits?
|
848
|
+
if (p > decimals_start) {
|
849
|
+
this->pushFloat(sign * n32 * pow10(exp));
|
850
|
+
return p;
|
851
|
+
}
|
852
|
+
return nullptr;
|
853
|
+
}
|
854
|
+
|
855
|
+
if (n32 > kMaxInt32) {
|
856
|
+
// we ran out on n32 bits
|
857
|
+
return this->matchFastFloatDecimalPart(p, sign, n32, exp);
|
858
|
+
}
|
859
|
+
}
|
860
|
+
|
861
|
+
return this->matchFastFloatPart(p, sign, n32);
|
862
|
+
}
|
863
|
+
|
864
|
+
const char* matchNumber(const char* p) {
|
865
|
+
if (const auto* fast = this->matchFast32OrFloat(p)) return fast;
|
866
|
+
|
867
|
+
// slow fallback
|
868
|
+
char* matched;
|
869
|
+
float f = strtof(p, &matched);
|
870
|
+
if (matched > p) {
|
871
|
+
this->pushFloat(f);
|
872
|
+
return matched;
|
873
|
+
}
|
874
|
+
return this->error(nullptr, p, "invalid numeric token");
|
875
|
+
}
|
876
|
+
};
|
877
|
+
|
878
|
+
void Write(const Value& v, SkWStream* stream) {
|
879
|
+
// We use the address of these as special tags in the pending list.
|
880
|
+
static const NullValue kArrayCloseTag, // ]
|
881
|
+
kObjectCloseTag, // }
|
882
|
+
kListSeparatorTag, // ,
|
883
|
+
kKeySeparatorTag; // :
|
884
|
+
|
885
|
+
std::vector<const Value*> pending{&v};
|
886
|
+
|
887
|
+
do {
|
888
|
+
const Value* val = pending.back();
|
889
|
+
pending.pop_back();
|
890
|
+
|
891
|
+
if (val == &kArrayCloseTag) {
|
892
|
+
stream->writeText("]");
|
893
|
+
continue;
|
894
|
+
}
|
895
|
+
|
896
|
+
if (val == &kObjectCloseTag) {
|
897
|
+
stream->writeText("}");
|
898
|
+
continue;
|
899
|
+
}
|
900
|
+
|
901
|
+
if (val == &kListSeparatorTag) {
|
902
|
+
stream->writeText(",");
|
903
|
+
continue;
|
904
|
+
}
|
905
|
+
|
906
|
+
if (val == &kKeySeparatorTag) {
|
907
|
+
stream->writeText(":");
|
908
|
+
continue;
|
909
|
+
}
|
910
|
+
|
911
|
+
switch (val->getType()) {
|
912
|
+
case Value::Type::kNull:
|
913
|
+
stream->writeText("null");
|
914
|
+
break;
|
915
|
+
case Value::Type::kBool:
|
916
|
+
stream->writeText(*val->as<BoolValue>() ? "true" : "false");
|
917
|
+
break;
|
918
|
+
case Value::Type::kNumber:
|
919
|
+
stream->writeScalarAsText(*val->as<NumberValue>());
|
920
|
+
break;
|
921
|
+
case Value::Type::kString:
|
922
|
+
stream->writeText("\"");
|
923
|
+
stream->writeText(val->as<StringValue>().begin());
|
924
|
+
stream->writeText("\"");
|
925
|
+
break;
|
926
|
+
case Value::Type::kArray: {
|
927
|
+
const auto& array = val->as<ArrayValue>();
|
928
|
+
stream->writeText("[");
|
929
|
+
// "val, val, .. ]" in reverse order
|
930
|
+
pending.push_back(&kArrayCloseTag);
|
931
|
+
if (array.size() > 0) {
|
932
|
+
bool last_value = true;
|
933
|
+
for (const Value* it = array.end() - 1; it >= array.begin(); --it) {
|
934
|
+
if (!last_value) pending.push_back(&kListSeparatorTag);
|
935
|
+
pending.push_back(it);
|
936
|
+
last_value = false;
|
937
|
+
}
|
938
|
+
}
|
939
|
+
} break;
|
940
|
+
case Value::Type::kObject: {
|
941
|
+
const auto& object = val->as<ObjectValue>();
|
942
|
+
stream->writeText("{");
|
943
|
+
// "key: val, key: val, .. }" in reverse order
|
944
|
+
pending.push_back(&kObjectCloseTag);
|
945
|
+
if (object.size() > 0) {
|
946
|
+
bool last_member = true;
|
947
|
+
for (const Member* it = object.end() - 1; it >= object.begin(); --it) {
|
948
|
+
if (!last_member) pending.push_back(&kListSeparatorTag);
|
949
|
+
pending.push_back(&it->fValue);
|
950
|
+
pending.push_back(&kKeySeparatorTag);
|
951
|
+
pending.push_back(&it->fKey);
|
952
|
+
last_member = false;
|
953
|
+
}
|
954
|
+
}
|
955
|
+
} break;
|
956
|
+
}
|
957
|
+
} while (!pending.empty());
|
958
|
+
}
|
959
|
+
|
960
|
+
} // namespace
|
961
|
+
|
962
|
+
SkString Value::toString() const {
|
963
|
+
SkDynamicMemoryWStream wstream;
|
964
|
+
Write(*this, &wstream);
|
965
|
+
const auto data = wstream.detachAsData();
|
966
|
+
// TODO: is there a better way to pass data around without copying?
|
967
|
+
return SkString(static_cast<const char*>(data->data()), data->size());
|
968
|
+
}
|
969
|
+
|
970
|
+
static constexpr size_t kMinChunkSize = 4096;
|
971
|
+
|
972
|
+
DOM::DOM(const char* data, size_t size) : fAlloc(kMinChunkSize) {
|
973
|
+
DOMParser parser(fAlloc);
|
974
|
+
|
975
|
+
fRoot = parser.parse(data, size);
|
976
|
+
}
|
977
|
+
|
978
|
+
void DOM::write(SkWStream* stream) const { Write(fRoot, stream); }
|
979
|
+
|
980
|
+
} // namespace skjson
|