14abe0d4bSJim Walker/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
24abe0d4bSJim Walker/*
34abe0d4bSJim Walker *     Copyright 2018 Couchbase, Inc.
44abe0d4bSJim Walker *
54abe0d4bSJim Walker *   Licensed under the Apache License, Version 2.0 (the "License");
64abe0d4bSJim Walker *   you may not use this file except in compliance with the License.
74abe0d4bSJim Walker *   You may obtain a copy of the License at
84abe0d4bSJim Walker *
94abe0d4bSJim Walker *       http://www.apache.org/licenses/LICENSE-2.0
104abe0d4bSJim Walker *
114abe0d4bSJim Walker *   Unless required by applicable law or agreed to in writing, software
124abe0d4bSJim Walker *   distributed under the License is distributed on an "AS IS" BASIS,
134abe0d4bSJim Walker *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
144abe0d4bSJim Walker *   See the License for the specific language governing permissions and
154abe0d4bSJim Walker *   limitations under the License.
164abe0d4bSJim Walker */
174abe0d4bSJim Walker#pragma once
184abe0d4bSJim Walker
1917b14c92SBen Huddleston#include <folly/Portability.h>
2017b14c92SBen Huddleston
214abe0d4bSJim Walker#include <algorithm>
224abe0d4bSJim Walker#include <array>
234abe0d4bSJim Walker
244abe0d4bSJim Walkernamespace cb {
254abe0d4bSJim Walker
264abe0d4bSJim Walker/**
274abe0d4bSJim Walker * UnsignedNByteInteger provides a non-atomic integral type for 3/5/6 or 7 byte
284abe0d4bSJim Walker * integers.
294abe0d4bSJim Walker *
304abe0d4bSJim Walker * All operations occur as unsigned 64-bit operations with the result stored
314abe0d4bSJim Walker * back to the smaller type with the most-significant bytes removed.
324abe0d4bSJim Walker *
334abe0d4bSJim Walker */
344abe0d4bSJim Walkertemplate <size_t N>
354abe0d4bSJim Walkerclass UnsignedNByteInteger {
364abe0d4bSJim Walker    static_assert(
374abe0d4bSJim Walker            (N < sizeof(uint64_t)),
384abe0d4bSJim Walker            "UnsignedNByteInteger: size should be less than sizeof(uint64_t)");
394abe0d4bSJim Walker    static_assert((N != sizeof(uint32_t)),
404abe0d4bSJim Walker                  "UnsignedNByteInteger: use uint32_t");
414abe0d4bSJim Walker    static_assert((N != sizeof(uint16_t)),
424abe0d4bSJim Walker                  "UnsignedNByteInteger: use uint16_t");
434abe0d4bSJim Walker    static_assert((N != sizeof(uint8_t)), "UnsignedNByteInteger: use uint8_t");
444abe0d4bSJim Walker
454abe0d4bSJim Walkerpublic:
464abe0d4bSJim Walker    /// Initialise to zero
474abe0d4bSJim Walker    UnsignedNByteInteger() : counter{} {
484abe0d4bSJim Walker    }
494abe0d4bSJim Walker
504abe0d4bSJim Walker    /// Initialise to n
514abe0d4bSJim Walker    UnsignedNByteInteger(uint64_t n) : counter{} {
524abe0d4bSJim Walker        store(n);
534abe0d4bSJim Walker    }
544abe0d4bSJim Walker
554abe0d4bSJim Walker    operator uint64_t() const {
564abe0d4bSJim Walker        return load();
574abe0d4bSJim Walker    }
584abe0d4bSJim Walker
594abe0d4bSJim Walker    UnsignedNByteInteger<N>& operator+=(uint64_t in) {
604abe0d4bSJim Walker        fetch_add(in);
614abe0d4bSJim Walker        return *this;
624abe0d4bSJim Walker    }
634abe0d4bSJim Walker
644abe0d4bSJim Walker    UnsignedNByteInteger<N>& operator-=(uint64_t in) {
654abe0d4bSJim Walker        fetch_sub(in);
664abe0d4bSJim Walker        return *this;
674abe0d4bSJim Walker    }
684abe0d4bSJim Walker
694abe0d4bSJim Walker    uint64_t operator++() {
704abe0d4bSJim Walker        return fetch_add(1) + 1;
714abe0d4bSJim Walker    }
724abe0d4bSJim Walker
734abe0d4bSJim Walker    uint64_t operator++(int) {
744abe0d4bSJim Walker        return fetch_add(1);
754abe0d4bSJim Walker    }
764abe0d4bSJim Walker
774abe0d4bSJim Walker    uint64_t operator--() {
784abe0d4bSJim Walker        return fetch_sub(1) - 1;
794abe0d4bSJim Walker    }
804abe0d4bSJim Walker
814abe0d4bSJim Walker    uint64_t operator--(int) {
824abe0d4bSJim Walker        return fetch_sub(1);
834abe0d4bSJim Walker    }
844abe0d4bSJim Walker
8517b14c92SBen Huddleston    UnsignedNByteInteger byteSwap() const {
8617b14c92SBen Huddleston        auto ret = UnsignedNByteInteger<N>();
8717b14c92SBen Huddleston        std::reverse_copy(counter.begin(), counter.end(), ret.counter.begin());
8817b14c92SBen Huddleston        return ret;
8917b14c92SBen Huddleston    }
9017b14c92SBen Huddleston
9117b14c92SBen Huddleston    UnsignedNByteInteger hton() const {
9217b14c92SBen Huddleston        if (folly::kIsLittleEndian) {
9317b14c92SBen Huddleston            return byteSwap();
9417b14c92SBen Huddleston        }
9517b14c92SBen Huddleston
9617b14c92SBen Huddleston        return *this;
9717b14c92SBen Huddleston    }
9817b14c92SBen Huddleston
9917b14c92SBen Huddleston    UnsignedNByteInteger ntoh() const {
10017b14c92SBen Huddleston        return hton();
10117b14c92SBen Huddleston    }
10217b14c92SBen Huddleston
1034abe0d4bSJim Walkerprivate:
1044abe0d4bSJim Walker    uint64_t fetch_add(uint64_t n) {
1054abe0d4bSJim Walker        uint64_t value = load();
1064abe0d4bSJim Walker        store(value + n);
1074abe0d4bSJim Walker        return value;
1084abe0d4bSJim Walker    }
1094abe0d4bSJim Walker
1104abe0d4bSJim Walker    uint64_t fetch_sub(uint64_t n) {
1114abe0d4bSJim Walker        uint64_t value = load();
1124abe0d4bSJim Walker        store(value - n);
1134abe0d4bSJim Walker        return value;
1144abe0d4bSJim Walker    }
1154abe0d4bSJim Walker
1164abe0d4bSJim Walker    /// @return the current value as a uint64_t
1174abe0d4bSJim Walker    uint64_t load() const {
1184abe0d4bSJim Walker        uint64_t value = 0;
1194abe0d4bSJim Walker        std::copy_n(counter.begin(), N, reinterpret_cast<uint8_t*>(&value));
1204abe0d4bSJim Walker        return value;
1214abe0d4bSJim Walker    }
1224abe0d4bSJim Walker
1234abe0d4bSJim Walker    void store(uint64_t value) {
1244abe0d4bSJim Walker        std::copy_n(reinterpret_cast<uint8_t*>(&value), N, counter.begin());
1254abe0d4bSJim Walker    }
1264abe0d4bSJim Walker
1274abe0d4bSJim Walker    /// The current value of the n-byte integer
1284abe0d4bSJim Walker    std::array<uint8_t, N> counter;
1294abe0d4bSJim Walker};
1304abe0d4bSJim Walker
1314abe0d4bSJim Walkerusing uint48_t = UnsignedNByteInteger<6>;
1324abe0d4bSJim Walker
1334abe0d4bSJim Walker} // namespace cb