Line data Source code
1 : /* 2 : * 3 : * Copyright (c) 2020-2021 Project CHIP Authors 4 : * 5 : * Licensed under the Apache License, Version 2.0 (the "License"); 6 : * you may not use this file except in compliance with the License. 7 : * You may obtain a copy of the License at 8 : * 9 : * http://www.apache.org/licenses/LICENSE-2.0 10 : * 11 : * Unless required by applicable law or agreed to in writing, software 12 : * distributed under the License is distributed on an "AS IS" BASIS, 13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 : * See the License for the specific language governing permissions and 15 : * limitations under the License. 16 : */ 17 : 18 : #pragma once 19 : 20 : #include <climits> 21 : #include <stdint.h> 22 : #include <string.h> 23 : 24 : #include <lib/support/Span.h> 25 : 26 : namespace chip { 27 : namespace Encoding { 28 : 29 : class BufferWriter 30 : { 31 : public: 32 22314 : BufferWriter(uint8_t * buf, size_t len) : mBuf(buf), mSize(len), mNeeded(0) 33 : { 34 22314 : if (buf == nullptr) 35 : { 36 150 : mSize = 0; 37 : } 38 22314 : } 39 224 : BufferWriter(MutableByteSpan buf) : BufferWriter(buf.data(), buf.size()) {} 40 : BufferWriter(const BufferWriter & other) = default; 41 : BufferWriter & operator=(const BufferWriter & other) = default; 42 : 43 : /// Append a null terminated string, exclude the null terminator 44 : BufferWriter & Put(const char * s); 45 : 46 : /// Raw append a buffer, regardless of endianess. 47 : /// This is memmove-safe: if `buf` points to the underlying buffer, where output 48 : /// will be written, and the overlap is legal for a memmove to have worked properly, 49 : /// then this method will properly copy data. 50 : BufferWriter & Put(const void * buf, size_t len); 51 : 52 : /// Append a single byte 53 : BufferWriter & Put(uint8_t c); 54 : 55 1816 : BufferWriter & Skip(size_t len) 56 : { 57 1816 : mNeeded += len; 58 1816 : return *this; 59 : } 60 : 61 : /// Number of bytes required to satisfy all calls to Put() so far 62 17200 : inline size_t Needed() const { return mNeeded; } 63 : 64 : /// Alias to Needed() for code clarity: current writing position for the buffer. 65 8252 : inline size_t WritePos() const { return Needed(); } 66 : 67 : /// Number of bytes still available for writing 68 10989 : size_t Available() const { return mSize < mNeeded ? 0 : mSize - mNeeded; } 69 : 70 : /// Whether the input fit in the buffer 71 30621 : bool Fit() const 72 : { 73 : size_t _; 74 30621 : return Fit(_); 75 : } 76 : 77 : /// Returns whether the input fit in the buffer, outputs what was actually written 78 30936 : bool Fit(size_t & actuallyWritten) const 79 : { 80 30936 : actuallyWritten = mSize >= mNeeded ? mNeeded : mSize; 81 30936 : return mSize >= mNeeded; 82 : } 83 : 84 : /// Size of the output buffer 85 26 : size_t Size() const { return mSize; } 86 : 87 24724 : uint8_t * Buffer() { return mBuf; } 88 668 : const uint8_t * Buffer() const { return mBuf; } 89 : 90 : BufferWriter & Format(const char * format, ...) ENFORCE_FORMAT(2, 3) 91 : { 92 : va_list args; 93 : va_start(args, format); 94 : VFormat(format, args); 95 : va_end(args); 96 : return *this; 97 : } 98 : 99 296 : void Reset() { mNeeded = 0; } 100 : 101 : /// Since this uses vsnprintf internally, on overflow 102 : /// this will write one less byte that strictly can be 103 : /// written (since null terminator will be in the binary data) 104 : BufferWriter & VFormat(const char * format, va_list args) ENFORCE_FORMAT(2, 0); 105 : 106 : /// Assume a specific size for the buffer instead of mSize 107 : /// 108 : /// This is to allow avoiding off-by-one overflow truncation 109 : /// when we know the underlying buffer size is larger. 110 : BufferWriter & VFormatWithSize(size_t size, const char * format, va_list args) ENFORCE_FORMAT(3, 0); 111 : 112 : protected: 113 : uint8_t * mBuf; 114 : size_t mSize; 115 : size_t mNeeded; 116 : }; 117 : 118 : template <class Derived> 119 : class EndianBufferWriterBase : public BufferWriter 120 : { 121 : public: 122 : // typed BufferWriter forwards 123 : 124 947 : Derived & Put(const char * s) { return static_cast<Derived &>(BufferWriter::Put(s)); } 125 437 : Derived & Put(const void * buf, size_t len) { return static_cast<Derived &>(BufferWriter::Put(buf, len)); } 126 256409 : Derived & Put(uint8_t c) { return static_cast<Derived &>(BufferWriter::Put(c)); } 127 110 : Derived & Skip(size_t len) { return static_cast<Derived &>(BufferWriter::Skip(len)); } 128 : 129 : // write an integer into a buffer, in an endian-specific way 130 : 131 20166 : Derived & Put8(uint8_t c) { return static_cast<Derived *>(this)->Put(c); } 132 3124 : Derived & Put16(uint16_t x) { return static_cast<Derived *>(this)->EndianPut(x, sizeof(x)); } 133 19922 : Derived & Put32(uint32_t x) { return static_cast<Derived *>(this)->EndianPut(x, sizeof(x)); } 134 18945 : Derived & Put64(uint64_t x) { return static_cast<Derived *>(this)->EndianPut(x, sizeof(x)); } 135 : 136 : Derived & PutSigned8(int8_t x) { return static_cast<Derived *>(this)->EndianPutSigned(x, sizeof(x)); } 137 : Derived & PutSigned16(int16_t x) { return static_cast<Derived *>(this)->EndianPutSigned(x, sizeof(x)); } 138 : Derived & PutSigned32(int32_t x) { return static_cast<Derived *>(this)->EndianPutSigned(x, sizeof(x)); } 139 : Derived & PutSigned64(int64_t x) { return static_cast<Derived *>(this)->EndianPutSigned(x, sizeof(x)); } 140 : 141 : protected: 142 19276 : EndianBufferWriterBase(uint8_t * buf, size_t len) : BufferWriter(buf, len) {} 143 40 : EndianBufferWriterBase(MutableByteSpan buf) : BufferWriter(buf.data(), buf.size()) {} 144 : EndianBufferWriterBase(const EndianBufferWriterBase & other) = default; 145 : EndianBufferWriterBase & operator=(const EndianBufferWriterBase & other) = default; 146 : }; 147 : 148 : namespace LittleEndian { 149 : 150 : class BufferWriter : public EndianBufferWriterBase<BufferWriter> 151 : { 152 : public: 153 19159 : BufferWriter(uint8_t * buf, size_t len) : EndianBufferWriterBase<BufferWriter>(buf, len) 154 : { 155 : static_assert((-1 & 3) == 3, "LittleEndian::BufferWriter only works with 2's complement architectures."); 156 19159 : } 157 40 : BufferWriter(MutableByteSpan buf) : EndianBufferWriterBase<BufferWriter>(buf) {} 158 : BufferWriter(const BufferWriter & other) = default; 159 : BufferWriter & operator=(const BufferWriter & other) = default; 160 : BufferWriter & EndianPut(uint64_t x, size_t size); 161 : BufferWriter & EndianPutSigned(int64_t x, size_t size); 162 : }; 163 : 164 : } // namespace LittleEndian 165 : 166 : namespace BigEndian { 167 : 168 : class BufferWriter : public EndianBufferWriterBase<BufferWriter> 169 : { 170 : public: 171 117 : BufferWriter(uint8_t * buf, size_t len) : EndianBufferWriterBase<BufferWriter>(buf, len) 172 : { 173 : static_assert((-1 & 3) == 3, "BigEndian::BufferWriter only works with 2's complement architectures."); 174 117 : } 175 : BufferWriter(MutableByteSpan buf) : EndianBufferWriterBase<BufferWriter>(buf) {} 176 : BufferWriter(const BufferWriter & other) = default; 177 : BufferWriter & operator=(const BufferWriter & other) = default; 178 : BufferWriter & EndianPut(uint64_t x, size_t size); 179 : BufferWriter & EndianPutSigned(int64_t x, size_t size); 180 : }; 181 : 182 : } // namespace BigEndian 183 : 184 : } // namespace Encoding 185 : } // namespace chip