Line data Source code
1 : /* 2 : * 3 : * Copyright (c) 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 <app/util/attribute-storage-null-handling.h> 21 : #include <lib/support/TypeTraits.h> 22 : 23 : #include <limits> 24 : 25 : namespace chip { 26 : namespace app { 27 : 28 : namespace detail { 29 : template <int ByteSize, bool IsSigned> 30 : struct WorkingTypeMapper 31 : { 32 : }; 33 : template <int ByteSize> 34 : struct WorkingTypeMapper<ByteSize, true> 35 : { 36 : using WorkingType = int64_t; 37 : }; 38 : template <> 39 : struct WorkingTypeMapper<3, true> 40 : { 41 : using WorkingType = int32_t; 42 : }; 43 : template <int ByteSize> 44 : struct WorkingTypeMapper<ByteSize, false> 45 : { 46 : using WorkingType = uint64_t; 47 : }; 48 : template <> 49 : struct WorkingTypeMapper<3, false> 50 : { 51 : using WorkingType = uint32_t; 52 : }; 53 : } // namespace detail 54 : 55 : template <int ByteSize, bool IsSigned> 56 : struct OddSizedInteger 57 : { 58 : // WorkingType is the type we use to represent the value as an actual 59 : // integer that we can do arithmetic, greater/less-than compares, etc on. 60 : using WorkingType = typename detail::WorkingTypeMapper<ByteSize, IsSigned>::WorkingType; 61 : 62 : // StorageType is the type "at rest" in the attribute store. It's a 63 : // native-endian byte buffer. 64 : using StorageType = uint8_t[ByteSize]; 65 : }; 66 : 67 : namespace detail { 68 : template <int ByteSize, bool IsBigEndian> 69 : struct IntegerByteIndexing; 70 : 71 : template <int ByteSize> 72 : struct IntegerByteIndexing<ByteSize, true> 73 : { 74 : static constexpr int highIndex = 0; 75 : static constexpr int lowIndex = ByteSize - 1; 76 : static constexpr int lowerIndex = 1; 77 : static constexpr int raiseIndex = -1; 78 : static constexpr int pastLowIndex = ByteSize; 79 : static constexpr int pastHighIndex = -1; 80 : }; 81 : 82 : template <int ByteSize> 83 : struct IntegerByteIndexing<ByteSize, false> 84 : { 85 : static constexpr int highIndex = ByteSize - 1; 86 : static constexpr int lowIndex = 0; 87 : static constexpr int lowerIndex = -1; 88 : static constexpr int raiseIndex = 1; 89 : static constexpr int pastLowIndex = -1; 90 : static constexpr int pastHighIndex = ByteSize; 91 : }; 92 : } // namespace detail 93 : 94 : template <int ByteSize, bool IsSigned, bool IsBigEndian> 95 : struct NumericAttributeTraits<OddSizedInteger<ByteSize, IsSigned>, IsBigEndian> : detail::IntegerByteIndexing<ByteSize, IsBigEndian> 96 : { 97 : using IntType = OddSizedInteger<ByteSize, IsSigned>; 98 : // StorageType is the type "at rest" in the attribute store. It's a 99 : // native-endian byte buffer. 100 : using StorageType = typename IntType::StorageType; 101 : // WorkingType is the type we use to represent the value as an actual 102 : // integer that we can do arithmetic, greater/less-than compares, etc on. 103 : using WorkingType = typename IntType::WorkingType; 104 : 105 : using Indexing = detail::IntegerByteIndexing<ByteSize, IsBigEndian>; 106 : using Indexing::highIndex; 107 : using Indexing::lowerIndex; 108 : using Indexing::lowIndex; 109 : using Indexing::pastHighIndex; 110 : using Indexing::pastLowIndex; 111 : using Indexing::raiseIndex; 112 : 113 0 : static constexpr WorkingType StorageToWorking(StorageType storageValue) 114 : { 115 : // WorkingType can always fit all of our bit-shifting, because it has at 116 : // least one extra byte. 117 0 : WorkingType value = 0; 118 0 : for (int i = highIndex; i != pastLowIndex; i += lowerIndex) 119 : { 120 0 : value = (value << 8) | storageValue[i]; 121 : } 122 : 123 : // If unsigned, we are done. If signed, we need to make sure our high 124 : // bit gets treated as a sign bit, not a value bit, with our bits in 2s 125 : // complement. 126 : if (IsSigned) 127 : { 128 0 : constexpr WorkingType MaxPositive = (static_cast<WorkingType>(1) << (8 * ByteSize - 1)) - 1; 129 0 : if (value > MaxPositive) 130 : { 131 0 : value = value - (static_cast<WorkingType>(1) << (8 * ByteSize)); 132 : } 133 : } 134 : 135 0 : return value; 136 : } 137 : 138 0 : static constexpr void WorkingToStorage(WorkingType workingValue, StorageType & storageValue) 139 : { 140 : // We can just grab the low ByteSize bytes of workingValue. 141 0 : for (int i = lowIndex; i != pastHighIndex; i += raiseIndex) 142 : { 143 : // Casting to uint8_t exactly grabs the lowest byte. 144 0 : storageValue[i] = static_cast<uint8_t>(workingValue); 145 0 : workingValue = workingValue >> 8; 146 : } 147 0 : } 148 : 149 0 : static constexpr bool IsNullValue(StorageType value) 150 : { 151 : if (IsSigned) 152 : { 153 : // Check for the equivalent of the most negative integer, in 2s 154 : // complement notation. 155 0 : if (value[highIndex] != 0x80) 156 : { 157 0 : return false; 158 : } 159 0 : for (int i = highIndex + lowerIndex; i != pastLowIndex; i += lowerIndex) 160 : { 161 0 : if (value[i] != 0x00) 162 : { 163 0 : return false; 164 : } 165 : } 166 0 : return true; 167 : } 168 : 169 : // Check for the equivalent of the largest unsigned integer. 170 0 : for (int i = highIndex; i != pastLowIndex; i += lowerIndex) 171 : { 172 0 : if (value[i] != 0xFF) 173 : { 174 0 : return false; 175 : } 176 : } 177 0 : return true; 178 : } 179 : 180 0 : static constexpr void SetNull(StorageType & value) 181 : { 182 : if (IsSigned) 183 : { 184 0 : value[highIndex] = 0x80; 185 0 : for (int i = highIndex + lowerIndex; i != pastLowIndex; i += lowerIndex) 186 : { 187 0 : value[i] = 0x00; 188 : } 189 : } 190 : else 191 : { 192 0 : for (int i = highIndex; i != pastLowIndex; i += lowerIndex) 193 : { 194 0 : value[i] = 0xFF; 195 : } 196 : } 197 0 : } 198 : 199 0 : static constexpr bool CanRepresentValue(bool isNullable, StorageType value) { return !isNullable || !IsNullValue(value); } 200 : 201 0 : static constexpr bool CanRepresentValue(bool isNullable, WorkingType value) 202 : { 203 : // Since WorkingType has at least one extra byte, none of our bitshifts 204 : // overflow. 205 : if (IsSigned) 206 : { 207 0 : WorkingType max = (static_cast<WorkingType>(1) << (8 * ByteSize - 1)) - 1; 208 0 : WorkingType min = -max; 209 0 : if (!isNullable) 210 : { 211 : // We have one more value. 212 0 : min -= 1; 213 : } 214 0 : return value >= min && value <= max; 215 : } 216 : 217 0 : WorkingType max = (static_cast<WorkingType>(1) << (8 * ByteSize)) - 1; 218 0 : if (isNullable) 219 : { 220 : // we have one less value 221 0 : max -= 1; 222 : } 223 0 : return value <= max; 224 : } 225 : 226 0 : static CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag, StorageType value) 227 : { 228 0 : return writer.Put(tag, StorageToWorking(value)); 229 : } 230 : 231 : static uint8_t * ToAttributeStoreRepresentation(StorageType & value) { return value; } 232 : }; 233 : 234 : } // namespace app 235 : } // namespace chip