summaryrefslogtreecommitdiff
path: root/include/tools/json_writer.hxx
blob: 700e82b9a2c63dde663c5a2f08135bc577193bc1 (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
/* -*- 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 <sal/types.h>

#include <string>
#include <string_view>
#include <utility>

/** 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;

public:
    JsonWriter();
    ~JsonWriter();

    [[nodiscard]] ScopedJsonWriterNode startNode(const char*);
    [[nodiscard]] ScopedJsonWriterArray startArray(const char*);
    [[nodiscard]] ScopedJsonWriterStruct startStruct();

    void put(const char* pPropName, const OUString& rPropValue);
    // Assumes utf-8 property value encoding
    void put(const char* pPropName, std::string_view rPropValue);
    void put(const char* pPropName, const char* pPropVal)
    {
        put(pPropName, std::string_view(pPropVal));
    }

    void put(const char* pPropName, sal_uInt16 nPropVal) { put(pPropName, sal_Int64(nPropVal)); }
    void put(const char* pPropName, sal_Int16 nPropVal) { put(pPropName, sal_Int64(nPropVal)); }
    void put(const char* pPropName, sal_Int32 nPropVal) { put(pPropName, sal_Int64(nPropVal)); }
    void put(const char* pPropName, sal_uInt32 nPropVal) { put(pPropName, sal_Int64(nPropVal)); }
    void put(const char* pPropName, sal_Int64);
    void put(const char* pPropName, bool);
    void put(const char* pPropName, double);

    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);

    /** Hands ownership of the underlying storage buffer to the caller,
     * after this no more document modifications may be written. */
    char* extractData() { return extractDataImpl().first; }
    OString extractAsOString();
    std::string extractAsStdString();

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

private:
    void endNode();
    void endArray();
    void endStruct();
    void addCommaBeforeField();
    void writeEscapedOUString(const OUString& rPropVal);
    std::pair<char*, int> extractDataImpl();
    void ensureSpace(int noMoreBytesRequired);

    // 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: */