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 "config.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 
85 private:
fetch_add(uint64_t n)86     uint64_t fetch_add(uint64_t n) {
87         uint64_t value = load();
88         store(value + n);
89         return value;
90     }
91 
fetch_sub(uint64_t n)92     uint64_t fetch_sub(uint64_t n) {
93         uint64_t value = load();
94         store(value - n);
95         return value;
96     }
97 
98     /// @return the current value as a uint64_t
load() const99     uint64_t load() const {
100         uint64_t value = 0;
101         std::copy_n(counter.begin(), N, reinterpret_cast<uint8_t*>(&value));
102         return value;
103     }
104 
store(uint64_t value)105     void store(uint64_t value) {
106         std::copy_n(reinterpret_cast<uint8_t*>(&value), N, counter.begin());
107     }
108 
109     /// The current value of the n-byte integer
110     std::array<uint8_t, N> counter;
111 };
112 
113 using uint48_t = UnsignedNByteInteger<6>;
114 
115 } // namespace cb
116