summaryrefslogtreecommitdiffstats
path: root/include/tools/json_writer.hxx
blob: c5faf542f1d8ee6a9a76f36e21b67df3fc32105b (plain)
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
#pragma once

#include <sal/config.h>

#include <tools/toolsdllapi.h>
#include <rtl/string.hxx>
#include <rtl/ustring.hxx>

#include <string_view>

/** Simple JSON encoder designed specifically for LibreOfficeKit purposes.
 *
 * (1) Minimal allocations/re-allocations/copying
 * (2) Small/simple JSON documents
 * (3) ascii property names
 */
namespace tools
{
class ScopedJsonWriterNode;
class ScopedJsonWriterArray;
class ScopedJsonWriterStruct;

class TOOLS_DLLPUBLIC JsonWriter
{
    friend class ScopedJsonWriterNode;
    friend class ScopedJsonWriterArray;
    friend class ScopedJsonWriterStruct;

    char* mpBuffer;
    char* mPos;
    int mSpaceAllocated;
    int mStartNodeCount;
    bool mbFirstFieldInNode;
    bool mbClosed; // cannot add to it anymore

public:
    JsonWriter();
    ~JsonWriter();

    [[nodiscard]] ScopedJsonWriterNode startNode(std::string_view);
    [[nodiscard]] ScopedJsonWriterArray startArray(std::string_view);
    [[nodiscard]] ScopedJsonWriterStruct startStruct();

    void put(std::u16string_view pPropName, const OUString& rPropValue);

    void put(std::string_view pPropName, const OUString& rPropValue);
    // Assumes utf-8 property value encoding
    void put(std::string_view pPropName, std::string_view rPropValue);
    void put(std::string_view pPropName, const char* pPropVal)
    {
        put(pPropName, std::string_view(pPropVal));
    }
    template <size_t N> void put(std::string_view pPropName, const char (&pPropVal)[N])
    {
        put(pPropName, std::string_view(pPropVal, N));
    }

    template <typename N, std::enable_if_t<std::is_arithmetic_v<N>, int> = 0>
    void put(std::string_view pPropName, N n)
    {
        putLiteral(pPropName, OString::number(n));
    }
    void put(std::string_view pPropName, bool);

    void putSimpleValue(const OUString& rPropValue);

    /// This assumes that this data belongs at this point in the stream, and is valid, and properly encoded
    void putRaw(std::string_view);

    /** Closes the tags, and returns data.
     * After this no more document modifications may be written. */
    OString finishAndGetAsOString();

    /** returns true if the current JSON data matches the string */
    bool isDataEquals(std::string_view) const;

private:
    void endNode();
    void endArray();
    void endStruct();
    void addCommaBeforeField();
    void writeEscapedOUString(const OUString& rPropVal);
    void closeDocument();
    void ensureSpace(int noMoreBytesRequired);
    void ensureSpaceAndWriteNameColon(std::string_view name, int valSize);
    void putLiteral(std::string_view propName, std::string_view propValue);

    // overflow validation in debug mode
    static constexpr unsigned char JSON_WRITER_DEBUG_MARKER = 0xde;

    inline void addValidationMark()
    {
#ifndef NDEBUG
        *(mpBuffer + mSpaceAllocated - 1) = JSON_WRITER_DEBUG_MARKER;
#endif
    }

    inline void validate()
    {
#ifndef NDEBUG
        unsigned char c = *(mpBuffer + mSpaceAllocated - 1);
        assert(c == JSON_WRITER_DEBUG_MARKER);
#endif
    }
};

/**
 * Auto-closes the node.
 */
class ScopedJsonWriterNode
{
    friend class JsonWriter;

    JsonWriter& mrWriter;

    ScopedJsonWriterNode(JsonWriter& rWriter)
        : mrWriter(rWriter)
    {
    }

public:
    ~ScopedJsonWriterNode() { mrWriter.endNode(); }
};

/**
 * Auto-closes the node.
 */
class ScopedJsonWriterArray
{
    friend class JsonWriter;

    JsonWriter& mrWriter;

    ScopedJsonWriterArray(JsonWriter& rWriter)
        : mrWriter(rWriter)
    {
    }

public:
    ~ScopedJsonWriterArray() { mrWriter.endArray(); }
};

/**
 * Auto-closes the node.
 */
class ScopedJsonWriterStruct
{
    friend class JsonWriter;

    JsonWriter& mrWriter;

    ScopedJsonWriterStruct(JsonWriter& rWriter)
        : mrWriter(rWriter)
    {
    }

public:
    ~ScopedJsonWriterStruct() { mrWriter.endStruct(); }
};
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */