summaryrefslogtreecommitdiff
path: root/src/u16.hh
blob: c23a366669925d3ed319fb261ba5d1793c5b9a0a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#ifndef U16_HH
#define U16_HH

#include <cstdint> // IWYU pragma: export
#include <expected>
#include <iterator>
#include <type_traits>

#include "u.hh" // IWYU pragma: export

namespace u16 {

template<std::forward_iterator T>
  requires std::is_same_v<std::iter_value_t<T>, uint16_t>
std::expected<uint32_t, u::ReadError> read(T& start, const T& end) {
  if (start == end) return std::unexpected(u::ReadError::End);
  uint16_t u = *start;
  if (u >= 0xd800 && u <= 0xdbff) {
    if (std::distance(start, end) < 2) {
      return std::unexpected(u::ReadError::Incomplete);
    }
    std::advance(start, 1);
    if (*start >= 0xdc00 && *start <= 0xdfff) {
      uint16_t v = *start;
      std::advance(start, 1);
      return 0x10000 + (((u - 0xd800) << 10) | (v - 0xdc00));
    }
    return std::unexpected(u::ReadError::Invalid);
  }
  std::advance(start, 1);
  if (u >= 0xdc00 && u <= 0xdfff) {
    return std::unexpected(u::ReadError::Invalid);
  }
  return u;
}

template<std::forward_iterator T>
  requires std::is_same_v<std::iter_value_t<T>, uint16_t>
std::expected<uint32_t, u::ReadErrorReplace> read_replace(T& start,
                                                          const T& end) {
  auto ret = read(start, end);
  if (ret.has_value())
    return *ret;
  switch (ret.error()) {
    case u::ReadError::Incomplete:
      return std::unexpected(u::ReadErrorReplace::Incomplete);
    case u::ReadError::End:
      return std::unexpected(u::ReadErrorReplace::End);
    case u::ReadError::Invalid:
      return 0xfffd;
  }
}

template<std::forward_iterator T>
  requires std::is_same_v<std::iter_value_t<T>, uint16_t>
bool write(T& start, const T& end, uint32_t code) {
  if (code < 0x10000) {
    if (start == end) return false;
    *start = static_cast<uint16_t>(code);
  } else {
    if (std::distance(start, end) < 2) return false;
    code -= 0x10000;
    *start = static_cast<uint16_t>(0xd800 + (code >> 10));
    std::advance(start, 1);
    *start = static_cast<uint16_t>(0xdc00 + (code & 0x3ff));
  }
  std::advance(start, 1);
  return true;
}

template<std::forward_iterator T>
  requires std::is_same_v<std::iter_value_t<T>, uint16_t>
bool skip(T& start, const T& end) {
  if (start == end) return false;
  if (*start >= 0xd800 && *start <= 0xdbff) {
    if (std::distance(start, end) < 2) return false;
    std::advance(start, 2);
    return true;
  }
  std::advance(start, 1);
  return true;
}

}  // namespace u16

#endif  // U16_HH