1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2018 Couchbase, Inc.
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 #pragma once
18 
19 #include <folly/Portability.h>
20 
21 #include <algorithm>
22 #include <array>
23 
24 namespace cb {
25 
26 /**
27  * UnsignedNByteInteger provides a non-atomic integral type for 3/5/6 or 7 byte
28  * integers.
29  *
30  * All operations occur as unsigned 64-bit operations with the result stored
31  * back to the smaller type with the most-significant bytes removed.
32  *
33  */
34 template <size_t N>
35 class UnsignedNByteInteger {
36     static_assert(
37             (N < sizeof(uint64_t)),
38             "UnsignedNByteInteger: size should be less than sizeof(uint64_t)");
39     static_assert((N != sizeof(uint32_t)),
40                   "UnsignedNByteInteger: use uint32_t");
41     static_assert((N != sizeof(uint16_t)),
42                   "UnsignedNByteInteger: use uint16_t");
43     static_assert((N != sizeof(uint8_t)), "UnsignedNByteInteger: use uint8_t");
44 
45 public:
46     /// Initialise to zero
UnsignedNByteInteger()47     UnsignedNByteInteger() : counter{} {
48     }
49 
50     /// Initialise to n
UnsignedNByteInteger(uint64_t n)51     UnsignedNByteInteger(uint64_t n) : counter{} {
52         store(n);
53     }
54 
operator uint64_t() const55     operator uint64_t() const {
56         return load();
57     }
58 
operator +=(uint64_t in)59     UnsignedNByteInteger<N>& operator+=(uint64_t in) {
60         fetch_add(in);
61         return *this;
62     }
63 
operator -=(uint64_t in)64     UnsignedNByteInteger<N>& operator-=(uint64_t in) {
65         fetch_sub(in);
66         return *this;
67     }
68 
operator ++()69     uint64_t operator++() {
70         return fetch_add(1) + 1;
71     }
72 
operator ++(int)73     uint64_t operator++(int) {
74         return fetch_add(1);
75     }
76 
operator --()77     uint64_t operator--() {
78         return fetch_sub(1) - 1;
79     }
80 
operator --(int)81     uint64_t operator--(int) {
82         return fetch_sub(1);
83     }
84 
byteSwap() const85     UnsignedNByteInteger byteSwap() const {
86         auto ret = UnsignedNByteInteger<N>();
87         std::reverse_copy(counter.begin(), counter.end(), ret.counter.begin());
88         return ret;
89     }
90 
hton() const91     UnsignedNByteInteger hton() const {
92         if (folly::kIsLittleEndian) {
93             return byteSwap();
94         }
95 
96         return *this;
97     }
98 
ntoh() const99     UnsignedNByteInteger ntoh() const {
100         return hton();
101     }
102 
103 private:
fetch_add(uint64_t n)104     uint64_t fetch_add(uint64_t n) {
105         uint64_t value = load();
106         store(value + n);
107         return value;
108     }
109 
fetch_sub(uint64_t n)110     uint64_t fetch_sub(uint64_t n) {
111         uint64_t value = load();
112         store(value - n);
113         return value;
114     }
115 
116     /// @return the current value as a uint64_t
load() const117     uint64_t load() const {
118         uint64_t value = 0;
119         std::copy_n(counter.begin(), N, reinterpret_cast<uint8_t*>(&value));
120         return value;
121     }
122 
store(uint64_t value)123     void store(uint64_t value) {
124         std::copy_n(reinterpret_cast<uint8_t*>(&value), N, counter.begin());
125     }
126 
127     /// The current value of the n-byte integer
128     std::array<uint8_t, N> counter;
129 };
130 
131 using uint48_t = UnsignedNByteInteger<6>;
132 
133 } // namespace cb
134