File: writing.md

package info (click to toggle)
glaze 6.5.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 7,948 kB
  • sloc: cpp: 121,839; sh: 99; ansic: 26; makefile: 13
file content (220 lines) | stat: -rw-r--r-- 6,790 bytes parent folder | download
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# Writing

Glaze provides a unified return type for all write operations that gives you both error information and the byte count written.

## The `error_ctx` Type

All write functions return `glz::error_ctx`:

```cpp
struct error_ctx {
    size_t count{};                          // Bytes written to output
    error_code ec{};                         // Error code (none on success)
    std::string_view custom_error_message{}; // Optional error details

    operator bool() const noexcept;          // Returns true when there IS an error
    bool operator==(error_code e) const noexcept;
};
```

### Key Properties

- **`count`**: Number of bytes written, even on error (useful for debugging)
- **`ec`**: The error code (`error_code::none` on success)
- **`operator bool()`**: Returns `true` when there is an error (matches `std::error_code` semantics)

## Basic Usage

### Writing to Resizable Buffers

```cpp
my_struct obj{};
std::string buffer{};
auto ec = glz::write_json(obj, buffer);
if (ec) {
    // Error occurred
    std::cerr << glz::format_error(ec, buffer) << '\n';
    return;
}
// Success: for resizable buffers, buffer.size() == ec.count
std::cout << "Wrote " << ec.count << " bytes\n";
```

### Writing to Fixed-Size Buffers

Fixed-size buffers like `std::array` and `std::span` have automatic bounds checking:

```cpp
std::array<char, 512> buffer;  // Minimum 512 bytes recommended
auto ec = glz::write_json(obj, buffer);
if (ec) {
    if (ec.ec == glz::error_code::buffer_overflow) {
        // Buffer was too small
        std::cerr << "Buffer overflow after " << ec.count << " bytes\n";

        // Use a larger buffer or the string-returning overload:
        auto result = glz::write_json(obj);
        if (result) {
            size_t required_size = result->size();
        }
    }
    return;
}
// Success: ec.count contains bytes written
std::string_view json(buffer.data(), ec.count);
```

### Writing to `std::span`

`std::span` is ideal for writing to external memory (shared memory, DMA buffers, memory-mapped files):

```cpp
std::span<char> shared_memory(ptr, size);
auto ec = glz::write_json(obj, shared_memory);
if (ec) {
    if (ec.ec == glz::error_code::buffer_overflow) {
        return error::insufficient_shared_memory;
    }
    return error::serialization_failed;
}
// Notify consumers of bytes written
notify_consumers(ec.count);
```

### Writing to Raw Pointers

Raw `char*` pointers are trusted to have sufficient space (no bounds checking):

```cpp
char* dma_buffer = get_dma_buffer();  // Caller guarantees sufficient space
auto ec = glz::write_json(obj, dma_buffer);
if (!ec) {
    trigger_dma_transfer(ec.count);
}
```

### Writing to Streams

For writing directly to files or output streams with bounded memory, see [Streaming I/O](streaming.md).

## Buffer Overflow Handling

When writing to a fixed-size buffer that's too small, Glaze returns `error_code::buffer_overflow`:

```cpp
std::array<char, 64> small_buffer;
auto ec = glz::write_json(large_object, small_buffer);
if (ec.ec == glz::error_code::buffer_overflow) {
    // ec.count contains bytes written before overflow
    // This is a LOWER BOUND on required size (not total required)

    // The partial content is NOT valid JSON - do not parse or transmit
    // Useful only for debugging:
    std::string_view partial(small_buffer.data(), ec.count);
    log_debug("Overflow after {} bytes: {}", ec.count, partial);

    // To get actual required size, use the string-returning overload:
    auto full = glz::write_json(large_object);
    if (full) {
        size_t required = full->size();
    }
}
```

### Important Notes on Buffer Overflow

1. **Content validity**: The first `ec.count` bytes contain valid serialized output
2. **Partial data is NOT valid**: The content is truncated mid-serialization and cannot be parsed
3. **`ec.count` is a lower bound**: It's the bytes written before failure, not total required size
4. **Content beyond `ec.count`**: Unspecified (may be uninitialized or previous data)

### Minimum Buffer Size Requirement

Bounded buffers (`std::array`, `std::span`, etc.) must be **at least 512 bytes** for reliable serialization. This requirement exists because Glaze uses internal padding for efficient writes. Buffers smaller than 512 bytes may return `buffer_overflow` even when the serialized output would fit.

```cpp
// Good: 512+ bytes
std::array<char, 512> buffer;
auto ec = glz::write_json(obj, buffer);

// May fail unexpectedly for small outputs
std::array<char, 64> small_buffer;  // Not recommended
```

For very small payloads where memory is extremely constrained, use a resizable buffer (`std::string`) and copy the result, or use `raw char*` if you can guarantee sufficient space.

## Performance Optimization

### Skipping Bounds Checking

For performance-critical paths where you've pre-validated buffer size:

```cpp
struct fast_opts : glz::opts {
    bool assume_sufficient_buffer = true;
};

std::array<char, 8192> large_buffer;  // Known to be sufficient
auto ec = glz::write<fast_opts{}>(obj, large_buffer);
// No bounds checking overhead
```

> **Warning**: Only use `assume_sufficient_buffer` when you've verified the buffer is large enough. Buffer overflow with this option leads to undefined behavior.

## Format Support

The `error_ctx` type is used consistently across all serialization formats:

```cpp
// JSON
auto ec = glz::write_json(obj, buffer);

// BEVE (Binary)
auto ec = glz::write_beve(obj, buffer);

// CBOR
auto ec = glz::write_cbor(obj, buffer);

// MessagePack
auto ec = glz::write_msgpack(obj, buffer);

// Generic with options
auto ec = glz::write<glz::opts{.format = glz::JSON}>(obj, buffer);
```

## Extending Buffer Support

Glaze uses a traits system that can be specialized for custom buffer types:

```cpp
namespace glz {
    template <size_t N>
    struct buffer_traits<my_lib::ring_buffer<N>> {
        static constexpr bool is_resizable = false;
        static constexpr bool has_bounded_capacity = true;

        static size_t capacity(const my_lib::ring_buffer<N>& b) noexcept {
            return b.available_write_space();
        }

        static bool ensure_capacity(my_lib::ring_buffer<N>& b, size_t needed) noexcept {
            return b.available_write_space() >= needed;
        }

        static void finalize(my_lib::ring_buffer<N>& b, size_t written) noexcept {
            b.commit(written);
        }
    };
}

// Now works seamlessly
my_lib::ring_buffer<4096> ring;
auto ec = glz::write_json(obj, ring);
```

## See Also

- [Reading](reading.md) - Reading from buffers
- [Streaming I/O](streaming.md) - Reading/writing with streams
- [Options](options.md) - Compile time options including `assume_sufficient_buffer`
- [Binary Format (BEVE)](binary.md) - Binary serialization