mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 16:18:19 -10:00
If invalid UTF-8 is encountered, return the standard Unicode replacement character 0xFFFD. When incrementing or decrementing, make a best effort to follow the code's instructions. It might produce one or more � characters, but that's better than an Error and a hard crash. Also move character constants to standardized locations.
194 lines
5.9 KiB
C++
194 lines
5.9 KiB
C++
#pragma once
|
|
|
|
#include "globalincs/pstypes.h"
|
|
|
|
#include "mod_table/mod_table.h"
|
|
|
|
#include <iterator>
|
|
#include <cinttypes>
|
|
|
|
#include <utf8.h>
|
|
|
|
#if !HAVE_CHAR32_T
|
|
// Older compilers don't have this as a built-in type so we define it for those
|
|
typedef std::uint_least32_t char32_t;
|
|
#endif
|
|
|
|
#if !HAVE_UNICODE_CHAR_LITERAL
|
|
#define UNICODE_CHAR(c) (char32_t) c
|
|
#else
|
|
/**
|
|
* @brief Compatibility macro for compilers which don't support U'' char literals.
|
|
*
|
|
* @warning This can only handle standard ASCII character values since older compilers still use standard character
|
|
* literals
|
|
*/
|
|
#define UNICODE_CHAR(c) U##c
|
|
#endif
|
|
|
|
namespace unicode {
|
|
|
|
/**
|
|
* @brief A standard unicode codepoint
|
|
*/
|
|
typedef char32_t codepoint_t;
|
|
|
|
/**
|
|
* @brief An invalid and ignorable character, equivalent to -1
|
|
*/
|
|
constexpr codepoint_t invalid_char = static_cast<codepoint_t>(-1);
|
|
|
|
/**
|
|
* @brief Substitute for malformed UTF-8 so that a bad decode can degrade gracefully
|
|
*/
|
|
constexpr codepoint_t replacement_char = 0xFFFD;
|
|
|
|
constexpr codepoint_t comment_char = static_cast<codepoint_t>(COMMENT_CHAR);
|
|
constexpr codepoint_t eoln = static_cast<codepoint_t>(EOLN);
|
|
constexpr codepoint_t carriage_return = static_cast<codepoint_t>(CARRIAGE_RETURN);
|
|
|
|
class text_iterator {
|
|
const char* current_byte = nullptr;
|
|
const char* range_end_byte = nullptr;
|
|
const char* range_start_byte = nullptr;
|
|
|
|
bool is_from_same_range(const text_iterator& other) const;
|
|
|
|
public:
|
|
explicit text_iterator(const char* current_byte, const char* range_start_byte, const char* range_end_byte = nullptr);
|
|
|
|
using difference_type = std::ptrdiff_t;
|
|
using value_type = codepoint_t;
|
|
using pointer = codepoint_t const *;
|
|
using reference = codepoint_t const &;
|
|
using iterator_category = std::bidirectional_iterator_tag;
|
|
|
|
const char* pos() const;
|
|
|
|
text_iterator& operator++();
|
|
text_iterator& operator--();
|
|
|
|
text_iterator operator++(int);
|
|
text_iterator operator--(int);
|
|
|
|
value_type operator*() const;
|
|
|
|
bool operator==(const text_iterator& rhs) const;
|
|
bool operator!=(const text_iterator& rhs) const;
|
|
|
|
bool operator<(const text_iterator& rhs) const;
|
|
bool operator>(const text_iterator& rhs) const;
|
|
bool operator<=(const text_iterator& rhs) const;
|
|
bool operator>=(const text_iterator& rhs) const;
|
|
|
|
text_iterator operator+(ptrdiff_t diff) const;
|
|
text_iterator operator-(ptrdiff_t diff) const;
|
|
};
|
|
|
|
/**
|
|
* @brief Represents a range of unicode codepoints that can be iterated over
|
|
*
|
|
* @note This class can be used in range based for loops
|
|
*/
|
|
class codepoint_range {
|
|
const char* start = nullptr;
|
|
const char* end_ptr = nullptr;
|
|
|
|
public:
|
|
/**
|
|
* @brief Creates a codepoint range based on an UTF-8 encoded string
|
|
* @param start The start of the encoded string
|
|
* @param end The end of the encoded string. May be @c nullptr in which case @c start is assumed to be null-terminated
|
|
*/
|
|
explicit codepoint_range(const char* start, const char* end = nullptr);
|
|
|
|
/**
|
|
* @brief Retrieves an iterator for the start of the codepoint range
|
|
* @return An iterator which is located at the start of the range
|
|
*/
|
|
text_iterator begin();
|
|
|
|
/**
|
|
* @brief Retrieves an iterator for the end of the codepoint range
|
|
* @return An iterator which is located at the end of the range
|
|
*/
|
|
text_iterator end();
|
|
};
|
|
|
|
/**
|
|
* @brief Computes the byte size the given codepoint would have if it were encoded using the standard encoding
|
|
*
|
|
* The standard encoding is UTF-8 in Unicode mode and ASCII otherwise.
|
|
*
|
|
* @param cp The codepoint to determine the size for.
|
|
* @return The number of bytes required for encoding this code point.
|
|
*/
|
|
size_t encoded_size(codepoint_t cp);
|
|
|
|
/**
|
|
* @brief Appends the given code point to the byte range specified by buffer.
|
|
*
|
|
* @c buffer can be a pointer to a character buffer or an output iterator. If the engine is in Unicode mode then the
|
|
* codepoint will be encoded using UTF-8. Otherwise the codepoint will be truncated to fit into the ASCII encoding.
|
|
*
|
|
* @tparam octet_iterator The type of the output sequence.
|
|
* @param cp The codepoint to encode
|
|
* @param buffer The buffer to write the encoded data to.
|
|
* @return The value of the iterator after appending all bytes to the sequence.
|
|
*/
|
|
template<typename octet_iterator>
|
|
octet_iterator encode(codepoint_t cp, octet_iterator buffer) {
|
|
if (Unicode_text_mode) {
|
|
try {
|
|
return utf8::append(cp, buffer);
|
|
} catch(const std::exception& e) {
|
|
Error(LOCATION, "Exception while encoding Unicode code point %" PRIu32 ": %s", (uint32_t)cp, e.what());
|
|
return buffer;
|
|
}
|
|
} else {
|
|
// In the legacy mode every code point is exactly one char
|
|
*(buffer++) = (char)cp;
|
|
return buffer;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Counts the number of code points in the specified byte sequence
|
|
*
|
|
* @note This respects the Unicode mod setting and should be used where the text contains Unicode characters.
|
|
*
|
|
* @tparam octet_iterator The type of the byte sequence.
|
|
* @param start An iterator located at the start of the sequence
|
|
* @param end An iterator which signals the end of the sequence
|
|
* @return The number of codepoints found between @c start and @c end
|
|
*/
|
|
template<typename octet_iterator>
|
|
size_t num_codepoints(octet_iterator start, octet_iterator end) {
|
|
if (Unicode_text_mode) {
|
|
try {
|
|
return static_cast<size_t>(utf8::distance(start, end));
|
|
} catch(const std::exception& e) {
|
|
Error(LOCATION, "Exception while counting Unicode code points: %s", e.what());
|
|
return 0;
|
|
}
|
|
} else {
|
|
return static_cast<size_t>(std::distance(start, end));
|
|
}
|
|
}
|
|
|
|
template<typename octet_iterator>
|
|
void advance(octet_iterator& start, size_t n, octet_iterator end) {
|
|
if (Unicode_text_mode) {
|
|
utf8::advance(start, n, end);
|
|
} else {
|
|
start = std::min(start + n, end);
|
|
}
|
|
}
|
|
|
|
enum class Encoding { Encoding_current, Encoding_utf8, Encoding_iso8859_1 };
|
|
|
|
bool string_is_ascii_only(const char* str, size_t len);
|
|
const char* get_encoding_string(Encoding encoding);
|
|
bool convert_encoding(SCP_string& buffer, const char* src, Encoding encoding_src, Encoding encoding_dest = Encoding::Encoding_current);
|
|
}
|