summaryrefslogtreecommitdiff
path: root/src/u16.hh
blob: 17a30f40a0c56b935ed798182cb93af4d523b6e0 (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
87
88
89
90
91
92
93
94
95
96
97
98
#ifndef U16_HH
#define U16_HH

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

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

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, T const& 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,
                                                          T const& end,
                                                          bool eof) {
  auto const tmp = start;
  auto ret = read(start, end);
  if (ret.has_value())
    return *ret;
  switch (ret.error()) {
    case u::ReadError::Incomplete:
      if (eof)
        break;
      return std::unexpected(u::ReadErrorReplace::Incomplete);
    case u::ReadError::End:
      return std::unexpected(u::ReadErrorReplace::End);
    case u::ReadError::Invalid:
      break;
  }
  start = tmp + 1;
  return 0xfffd;
}

template <std::forward_iterator T>
  requires std::is_same_v<std::iter_value_t<T>, uint16_t>
bool write(T& start, T const& 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, T const& 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