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
|