mirror of
https://github.com/stefanocasazza/ULib.git
synced 2025-09-28 19:05:55 +08:00
2638 lines
66 KiB
C++
2638 lines
66 KiB
C++
// ============================================================================
|
|
//
|
|
// = LIBRARY
|
|
// ULib - c++ library
|
|
//
|
|
// = FILENAME
|
|
// string.cpp
|
|
//
|
|
// = AUTHOR
|
|
// Stefano Casazza
|
|
//
|
|
// ============================================================================
|
|
|
|
#include <ulib/file.h>
|
|
#include <ulib/utility/escape.h>
|
|
#include <ulib/internal/chttp.h>
|
|
#include <ulib/container/hash_map.h>
|
|
|
|
struct ustring { ustringrep* rep; };
|
|
union uustring { ustring* p1; UString* p2; };
|
|
union uustringrep { ustringrep* p1; UStringRep* p2; };
|
|
|
|
static ustring empty_string_storage = { &u_empty_string_rep_storage };
|
|
static uustringrep uustringrepnull = { &u_empty_string_rep_storage };
|
|
static uustring uustringnull = { &empty_string_storage };
|
|
|
|
UString* UString::string_null = uustringnull.p2;
|
|
UStringRep* UStringRep::string_rep_null = uustringrepnull.p2;
|
|
|
|
// OPTMIZE APPEND (BUFFERED)
|
|
char* UString::appbuf;
|
|
char* UString::ptrbuf;
|
|
|
|
const UString* UString::str_host;
|
|
const UString* UString::str_cookie;
|
|
const UString* UString::str_connection;
|
|
const UString* UString::str_user_agent;
|
|
const UString* UString::str_authorization;
|
|
const UString* UString::str_content_type;
|
|
const UString* UString::str_content_length;
|
|
const UString* UString::str_accept;
|
|
const UString* UString::str_accept_encoding;
|
|
const UString* UString::str_referer;
|
|
const UString* UString::str_X_Real_IP;
|
|
const UString* UString::str_Transfer_Encoding;
|
|
const UString* UString::str_X_Progress_ID;
|
|
const UString* UString::str_chunked;
|
|
const UString* UString::str_without_mac;
|
|
const UString* UString::str_encoding;
|
|
const UString* UString::str_user;
|
|
const UString* UString::str_name;
|
|
const UString* UString::str_localhost;
|
|
const UString* UString::str_http;
|
|
const UString* UString::str_filename;
|
|
const UString* UString::str_msg_rfc;
|
|
const UString* UString::str_txt_plain;
|
|
const UString* UString::str_address;
|
|
const UString* UString::str_ns;
|
|
const UString* UString::str_METHOD_NAME;
|
|
const UString* UString::str_RESPONSE_TYPE;
|
|
const UString* UString::str_xmlns;
|
|
const UString* UString::str_fault;
|
|
const UString* UString::str_boolean;
|
|
const UString* UString::str_byte;
|
|
const UString* UString::str_unsignedByte;
|
|
const UString* UString::str_short;
|
|
const UString* UString::str_unsignedShort;
|
|
const UString* UString::str_int;
|
|
const UString* UString::str_unsignedInt;
|
|
const UString* UString::str_long;
|
|
const UString* UString::str_unsignedLong;
|
|
const UString* UString::str_float;
|
|
const UString* UString::str_double;
|
|
const UString* UString::str_string;
|
|
const UString* UString::str_base64Binary;
|
|
const UString* UString::str_PORT;
|
|
const UString* UString::str_USER;
|
|
const UString* UString::str_SERVER;
|
|
const UString* UString::str_CA_FILE;
|
|
const UString* UString::str_CA_PATH;
|
|
const UString* UString::str_PASSWORD;
|
|
const UString* UString::str_KEY_FILE;
|
|
const UString* UString::str_PID_FILE;
|
|
const UString* UString::str_LOG_FILE;
|
|
const UString* UString::str_CERT_FILE;
|
|
const UString* UString::str_LOG_FILE_SZ;
|
|
const UString* UString::str_VERIFY_MODE;
|
|
const UString* UString::str_SOCKET_NAME;
|
|
const UString* UString::str_ENVIRONMENT;
|
|
const UString* UString::str_CLIENT_QUEUE_DIR;
|
|
const UString* UString::str_point;
|
|
const UString* UString::str_true;
|
|
const UString* UString::str_false;
|
|
|
|
void UString::str_allocate()
|
|
{
|
|
U_TRACE(0+256, "UString::str_allocate()")
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(str_host, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_cookie, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_connection, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_user_agent, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_authorization, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_content_type, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_content_length, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_accept, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_accept_encoding, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_referer, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_X_Real_IP, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_Transfer_Encoding, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_X_Progress_ID, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_chunked, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_without_mac, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_encoding, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_user, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_name, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_localhost, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_http, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_filename, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_msg_rfc, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_txt_plain, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_address, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_ns, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_METHOD_NAME, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_RESPONSE_TYPE, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_xmlns, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_fault, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_boolean, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_byte, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_unsignedByte, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_short, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_unsignedShort, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_int, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_unsignedInt, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_long, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_unsignedLong, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_float, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_double, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_string, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_base64Binary, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_PORT, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_USER, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_SERVER, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_CA_FILE, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_CA_PATH, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_PASSWORD, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_KEY_FILE, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_PID_FILE, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_LOG_FILE, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_CERT_FILE, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_LOG_FILE_SZ, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_VERIFY_MODE, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_SOCKET_NAME, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_ENVIRONMENT, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_CLIENT_QUEUE_DIR, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_point, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_true, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(str_false, 0)
|
|
U_INTERNAL_ASSERT_EQUALS(UHashMap<void*>::pkey, 0)
|
|
|
|
static ustringrep stringrep_storage[] = {
|
|
{ U_STRINGREP_FROM_CONSTANT("Host") },
|
|
{ U_STRINGREP_FROM_CONSTANT("Cookie") },
|
|
{ U_STRINGREP_FROM_CONSTANT("Connection") },
|
|
{ U_STRINGREP_FROM_CONSTANT("User-Agent") },
|
|
{ U_STRINGREP_FROM_CONSTANT("Authorization") },
|
|
{ U_STRINGREP_FROM_CONSTANT("Content-Type") },
|
|
{ U_STRINGREP_FROM_CONSTANT("Content-Length") },
|
|
{ U_STRINGREP_FROM_CONSTANT("Accept") },
|
|
{ U_STRINGREP_FROM_CONSTANT("Accept-Encoding") },
|
|
{ U_STRINGREP_FROM_CONSTANT("Referer") },
|
|
{ U_STRINGREP_FROM_CONSTANT("X-Real-IP") },
|
|
{ U_STRINGREP_FROM_CONSTANT("Transfer-Encoding") },
|
|
{ U_STRINGREP_FROM_CONSTANT("X-Progress-ID") },
|
|
{ U_STRINGREP_FROM_CONSTANT("chunked") },
|
|
{ U_STRINGREP_FROM_CONSTANT("00:00:00:00:00:00") },
|
|
{ U_STRINGREP_FROM_CONSTANT("encoding") },
|
|
{ U_STRINGREP_FROM_CONSTANT("user") },
|
|
{ U_STRINGREP_FROM_CONSTANT("name") },
|
|
{ U_STRINGREP_FROM_CONSTANT("localhost") },
|
|
{ U_STRINGREP_FROM_CONSTANT("http") },
|
|
{ U_STRINGREP_FROM_CONSTANT("filename") },
|
|
{ U_STRINGREP_FROM_CONSTANT("message/rfc822") },
|
|
{ U_STRINGREP_FROM_CONSTANT("text/plain") },
|
|
{ U_STRINGREP_FROM_CONSTANT("stefano.casazza@gmail.com") },
|
|
{ U_STRINGREP_FROM_CONSTANT("ns") },
|
|
{ U_STRINGREP_FROM_CONSTANT("METHOD_NAME") },
|
|
{ U_STRINGREP_FROM_CONSTANT("RESPONSE_TYPE") },
|
|
{ U_STRINGREP_FROM_CONSTANT("xmlns") },
|
|
{ U_STRINGREP_FROM_CONSTANT("Fault") },
|
|
{ U_STRINGREP_FROM_CONSTANT("boolean") },
|
|
{ U_STRINGREP_FROM_CONSTANT("byte") },
|
|
{ U_STRINGREP_FROM_CONSTANT("unsignedByte") },
|
|
{ U_STRINGREP_FROM_CONSTANT("short") },
|
|
{ U_STRINGREP_FROM_CONSTANT("unsignedShort") },
|
|
{ U_STRINGREP_FROM_CONSTANT("int") },
|
|
{ U_STRINGREP_FROM_CONSTANT("unsignedInt") },
|
|
{ U_STRINGREP_FROM_CONSTANT("long") },
|
|
{ U_STRINGREP_FROM_CONSTANT("unsignedLong") },
|
|
{ U_STRINGREP_FROM_CONSTANT("float") },
|
|
{ U_STRINGREP_FROM_CONSTANT("double") },
|
|
{ U_STRINGREP_FROM_CONSTANT("string") },
|
|
{ U_STRINGREP_FROM_CONSTANT("base64Binary") },
|
|
{ U_STRINGREP_FROM_CONSTANT("PORT") },
|
|
{ U_STRINGREP_FROM_CONSTANT("USER") },
|
|
{ U_STRINGREP_FROM_CONSTANT("SERVER") },
|
|
{ U_STRINGREP_FROM_CONSTANT("CA_FILE") },
|
|
{ U_STRINGREP_FROM_CONSTANT("CA_PATH") },
|
|
{ U_STRINGREP_FROM_CONSTANT("PASSWORD") },
|
|
{ U_STRINGREP_FROM_CONSTANT("KEY_FILE") },
|
|
{ U_STRINGREP_FROM_CONSTANT("PID_FILE") },
|
|
{ U_STRINGREP_FROM_CONSTANT("LOG_FILE") },
|
|
{ U_STRINGREP_FROM_CONSTANT("CERT_FILE") },
|
|
{ U_STRINGREP_FROM_CONSTANT("LOG_FILE_SZ") },
|
|
{ U_STRINGREP_FROM_CONSTANT("VERIFY_MODE") },
|
|
{ U_STRINGREP_FROM_CONSTANT("SOCKET_NAME") },
|
|
{ U_STRINGREP_FROM_CONSTANT("ENVIRONMENT") },
|
|
{ U_STRINGREP_FROM_CONSTANT("/tmp/uclient") },
|
|
{ U_STRINGREP_FROM_CONSTANT(".") },
|
|
{ U_STRINGREP_FROM_CONSTANT("true") },
|
|
{ U_STRINGREP_FROM_CONSTANT("false") },
|
|
{ U_STRINGREP_FROM_CONSTANT("") }
|
|
};
|
|
|
|
U_NEW_ULIB_OBJECT(str_host, U_STRING_FROM_STRINGREP_STORAGE(0));
|
|
U_NEW_ULIB_OBJECT(str_cookie, U_STRING_FROM_STRINGREP_STORAGE(1));
|
|
U_NEW_ULIB_OBJECT(str_connection, U_STRING_FROM_STRINGREP_STORAGE(2));
|
|
U_NEW_ULIB_OBJECT(str_user_agent, U_STRING_FROM_STRINGREP_STORAGE(3));
|
|
U_NEW_ULIB_OBJECT(str_authorization, U_STRING_FROM_STRINGREP_STORAGE(4));
|
|
U_NEW_ULIB_OBJECT(str_content_type, U_STRING_FROM_STRINGREP_STORAGE(5));
|
|
U_NEW_ULIB_OBJECT(str_content_length, U_STRING_FROM_STRINGREP_STORAGE(6));
|
|
U_NEW_ULIB_OBJECT(str_accept, U_STRING_FROM_STRINGREP_STORAGE(7));
|
|
U_NEW_ULIB_OBJECT(str_accept_encoding, U_STRING_FROM_STRINGREP_STORAGE(8));
|
|
U_NEW_ULIB_OBJECT(str_referer, U_STRING_FROM_STRINGREP_STORAGE(9));
|
|
U_NEW_ULIB_OBJECT(str_X_Real_IP, U_STRING_FROM_STRINGREP_STORAGE(10));
|
|
U_NEW_ULIB_OBJECT(str_Transfer_Encoding, U_STRING_FROM_STRINGREP_STORAGE(11));
|
|
U_NEW_ULIB_OBJECT(str_X_Progress_ID, U_STRING_FROM_STRINGREP_STORAGE(12));
|
|
U_NEW_ULIB_OBJECT(str_chunked, U_STRING_FROM_STRINGREP_STORAGE(13));
|
|
U_NEW_ULIB_OBJECT(str_without_mac, U_STRING_FROM_STRINGREP_STORAGE(14));
|
|
U_NEW_ULIB_OBJECT(str_encoding, U_STRING_FROM_STRINGREP_STORAGE(15));
|
|
U_NEW_ULIB_OBJECT(str_user, U_STRING_FROM_STRINGREP_STORAGE(16));
|
|
U_NEW_ULIB_OBJECT(str_name, U_STRING_FROM_STRINGREP_STORAGE(17));
|
|
U_NEW_ULIB_OBJECT(str_localhost, U_STRING_FROM_STRINGREP_STORAGE(18));
|
|
U_NEW_ULIB_OBJECT(str_http, U_STRING_FROM_STRINGREP_STORAGE(19));
|
|
U_NEW_ULIB_OBJECT(str_filename, U_STRING_FROM_STRINGREP_STORAGE(20));
|
|
U_NEW_ULIB_OBJECT(str_msg_rfc, U_STRING_FROM_STRINGREP_STORAGE(21));
|
|
U_NEW_ULIB_OBJECT(str_txt_plain, U_STRING_FROM_STRINGREP_STORAGE(22));
|
|
U_NEW_ULIB_OBJECT(str_address, U_STRING_FROM_STRINGREP_STORAGE(23));
|
|
U_NEW_ULIB_OBJECT(str_ns, U_STRING_FROM_STRINGREP_STORAGE(24));
|
|
U_NEW_ULIB_OBJECT(str_METHOD_NAME, U_STRING_FROM_STRINGREP_STORAGE(25));
|
|
U_NEW_ULIB_OBJECT(str_RESPONSE_TYPE, U_STRING_FROM_STRINGREP_STORAGE(26));
|
|
U_NEW_ULIB_OBJECT(str_xmlns, U_STRING_FROM_STRINGREP_STORAGE(27));
|
|
U_NEW_ULIB_OBJECT(str_fault, U_STRING_FROM_STRINGREP_STORAGE(28));
|
|
U_NEW_ULIB_OBJECT(str_boolean, U_STRING_FROM_STRINGREP_STORAGE(29));
|
|
U_NEW_ULIB_OBJECT(str_byte, U_STRING_FROM_STRINGREP_STORAGE(30));
|
|
U_NEW_ULIB_OBJECT(str_unsignedByte, U_STRING_FROM_STRINGREP_STORAGE(31));
|
|
U_NEW_ULIB_OBJECT(str_short, U_STRING_FROM_STRINGREP_STORAGE(32));
|
|
U_NEW_ULIB_OBJECT(str_unsignedShort, U_STRING_FROM_STRINGREP_STORAGE(33));
|
|
U_NEW_ULIB_OBJECT(str_int, U_STRING_FROM_STRINGREP_STORAGE(34));
|
|
U_NEW_ULIB_OBJECT(str_unsignedInt, U_STRING_FROM_STRINGREP_STORAGE(35));
|
|
U_NEW_ULIB_OBJECT(str_long, U_STRING_FROM_STRINGREP_STORAGE(36));
|
|
U_NEW_ULIB_OBJECT(str_unsignedLong, U_STRING_FROM_STRINGREP_STORAGE(37));
|
|
U_NEW_ULIB_OBJECT(str_float, U_STRING_FROM_STRINGREP_STORAGE(38));
|
|
U_NEW_ULIB_OBJECT(str_double, U_STRING_FROM_STRINGREP_STORAGE(39));
|
|
U_NEW_ULIB_OBJECT(str_string, U_STRING_FROM_STRINGREP_STORAGE(40));
|
|
U_NEW_ULIB_OBJECT(str_base64Binary, U_STRING_FROM_STRINGREP_STORAGE(41));
|
|
U_NEW_ULIB_OBJECT(str_PORT, U_STRING_FROM_STRINGREP_STORAGE(42));
|
|
U_NEW_ULIB_OBJECT(str_USER, U_STRING_FROM_STRINGREP_STORAGE(43));
|
|
U_NEW_ULIB_OBJECT(str_SERVER, U_STRING_FROM_STRINGREP_STORAGE(44));
|
|
U_NEW_ULIB_OBJECT(str_CA_FILE, U_STRING_FROM_STRINGREP_STORAGE(45));
|
|
U_NEW_ULIB_OBJECT(str_CA_PATH, U_STRING_FROM_STRINGREP_STORAGE(46));
|
|
U_NEW_ULIB_OBJECT(str_PASSWORD, U_STRING_FROM_STRINGREP_STORAGE(47));
|
|
U_NEW_ULIB_OBJECT(str_KEY_FILE, U_STRING_FROM_STRINGREP_STORAGE(48));
|
|
U_NEW_ULIB_OBJECT(str_PID_FILE, U_STRING_FROM_STRINGREP_STORAGE(49));
|
|
U_NEW_ULIB_OBJECT(str_LOG_FILE, U_STRING_FROM_STRINGREP_STORAGE(50));
|
|
U_NEW_ULIB_OBJECT(str_CERT_FILE, U_STRING_FROM_STRINGREP_STORAGE(51));
|
|
U_NEW_ULIB_OBJECT(str_LOG_FILE_SZ, U_STRING_FROM_STRINGREP_STORAGE(52));
|
|
U_NEW_ULIB_OBJECT(str_VERIFY_MODE, U_STRING_FROM_STRINGREP_STORAGE(53));
|
|
U_NEW_ULIB_OBJECT(str_SOCKET_NAME, U_STRING_FROM_STRINGREP_STORAGE(54));
|
|
U_NEW_ULIB_OBJECT(str_ENVIRONMENT, U_STRING_FROM_STRINGREP_STORAGE(55));
|
|
U_NEW_ULIB_OBJECT(str_CLIENT_QUEUE_DIR, U_STRING_FROM_STRINGREP_STORAGE(56));
|
|
U_NEW_ULIB_OBJECT(str_point, U_STRING_FROM_STRINGREP_STORAGE(57));
|
|
U_NEW_ULIB_OBJECT(str_true, U_STRING_FROM_STRINGREP_STORAGE(58));
|
|
U_NEW_ULIB_OBJECT(str_false, U_STRING_FROM_STRINGREP_STORAGE(59));
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(*str_without_mac, "00:00:00:00:00:00")
|
|
U_INTERNAL_ASSERT_EQUALS(*str_CLIENT_QUEUE_DIR, "/tmp/uclient")
|
|
U_INTERNAL_ASSERT_EQUALS(U_NUM_ELEMENTS(stringrep_storage), 61)
|
|
|
|
uustringrep key1 = { stringrep_storage+60 };
|
|
|
|
UHashMap<void*>::pkey = key1.p2;
|
|
|
|
U_INTERNAL_ASSERT(UHashMap<void*>::pkey->invariant())
|
|
}
|
|
|
|
U_NO_EXPORT void UStringRep::set(uint32_t __length, uint32_t __capacity, const char* ptr)
|
|
{
|
|
// U_TRACE(0, "UStringRep::set(%u,%u,%p)", __length, __capacity, ptr) // problem with sanitize address
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
U_INTERNAL_ASSERT_POINTER(ptr)
|
|
|
|
#if defined(U_SUBSTR_INC_REF) || defined(DEBUG)
|
|
parent = 0;
|
|
# ifdef DEBUG
|
|
child = 0;
|
|
# endif
|
|
#endif
|
|
|
|
_length = __length;
|
|
_capacity = __capacity; // [0 const | -1 mmap]...
|
|
references = 0;
|
|
str = ptr;
|
|
}
|
|
|
|
UStringRep::UStringRep(const char* t, uint32_t tlen)
|
|
{
|
|
U_TRACE_REGISTER_OBJECT(0, UStringRep, "%.*S,%u", tlen, t, tlen) // problem with sanitize address
|
|
|
|
U_INTERNAL_ASSERT_POINTER(t)
|
|
U_INTERNAL_ASSERT_MAJOR(tlen, 0)
|
|
|
|
set(tlen, 0U, t);
|
|
}
|
|
|
|
// NB: ctor is private...
|
|
|
|
UStringRep::~UStringRep()
|
|
{
|
|
U_TRACE(0, "UStringRep::~UStringRep()")
|
|
|
|
// NB: we don't use delete (dtor) because it add a deallocation to the destroy process...
|
|
|
|
U_ERROR("I can't use UStringRep on stack");
|
|
}
|
|
|
|
UStringRep* UStringRep::create(uint32_t length, uint32_t need, const char* ptr)
|
|
{
|
|
U_TRACE(1, "UStringRep::create(%u,%u,%p)", length, need, ptr)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(need, 0)
|
|
|
|
char* _ptr;
|
|
UStringRep* r;
|
|
|
|
// NB: we don't use new (ctor) because we want an allocation with more space for string data...
|
|
|
|
#ifndef ENABLE_MEMPOOL
|
|
r = (UStringRep*) U_SYSCALL(malloc, "%u", need+(1+sizeof(UStringRep)));
|
|
_ptr = (char*)(r + 1);
|
|
#else
|
|
if (need > U_CAPACITY)
|
|
{
|
|
_ptr = UFile::mmap(&need, -1, PROT_READ | PROT_WRITE, U_MAP_ANON, 0);
|
|
|
|
if (_ptr == MAP_FAILED) U_RETURN_POINTER(string_rep_null, UStringRep);
|
|
|
|
r = U_MALLOC_TYPE(UStringRep);
|
|
}
|
|
else
|
|
{
|
|
# ifdef DEBUG
|
|
UMemoryPool::obj_class = "UStringRep";
|
|
UMemoryPool::func_call = __PRETTY_FUNCTION__;
|
|
# endif
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------------------
|
|
// see: http://www.codeproject.com/Articles/702065/C-Struct-Hack
|
|
//
|
|
// NB: we need an array of char[_capacity], plus a terminating null char element, plus enough for the UStringRep data structure...
|
|
// -------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
if (need > (U_STACK_TYPE_8-(1+sizeof(UStringRep))))
|
|
{
|
|
need = U_STACK_TYPE_9-(1+sizeof(UStringRep));
|
|
|
|
r = (UStringRep*) UMemoryPool::pop(9);
|
|
}
|
|
else
|
|
{
|
|
int stack_index;
|
|
uint32_t sz = need + (1+sizeof(UStringRep));
|
|
|
|
if (sz <= U_STACK_TYPE_4) // 128
|
|
{
|
|
need = U_STACK_TYPE_4-(1+sizeof(UStringRep));
|
|
stack_index = 4;
|
|
}
|
|
else if (sz <= U_STACK_TYPE_5) // 256
|
|
{
|
|
need = U_STACK_TYPE_5-(1+sizeof(UStringRep));
|
|
stack_index = 5;
|
|
}
|
|
else if (sz <= U_STACK_TYPE_6) // 512
|
|
{
|
|
need = U_STACK_TYPE_6-(1+sizeof(UStringRep));
|
|
stack_index = 6;
|
|
}
|
|
else if (sz <= U_STACK_TYPE_7) // 1024
|
|
{
|
|
need = U_STACK_TYPE_7-(1+sizeof(UStringRep));
|
|
stack_index = 7;
|
|
}
|
|
else
|
|
{
|
|
U_INTERNAL_ASSERT(need <= U_STACK_TYPE_8) // 2048
|
|
|
|
need = U_STACK_TYPE_8-(1+sizeof(UStringRep));
|
|
stack_index = 8;
|
|
}
|
|
|
|
U_INTERNAL_DUMP("sz = %u need = %u stack_index = %u", sz, need, stack_index)
|
|
|
|
r = (UStringRep*) UMemoryPool::pop(stack_index);
|
|
}
|
|
|
|
_ptr = (char*)(r + 1);
|
|
|
|
# ifdef DEBUG
|
|
UMemoryPool::obj_class = UMemoryPool::func_call = 0;
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
U_SET_LOCATION_INFO;
|
|
U_REGISTER_OBJECT_PTR(0,UStringRep,r,&(r->memory._this))
|
|
r->memory._this = (void*)U_CHECK_MEMORY_SENTINEL;
|
|
#endif
|
|
|
|
r->set(length, need, _ptr);
|
|
|
|
if (length &&
|
|
ptr)
|
|
{
|
|
U_MEMCPY((void*)_ptr, ptr, length);
|
|
|
|
_ptr[length] = '\0';
|
|
}
|
|
|
|
U_INTERNAL_ASSERT(r->invariant())
|
|
|
|
U_RETURN_POINTER(r, UStringRep);
|
|
}
|
|
|
|
bool UString::shrink()
|
|
{
|
|
U_TRACE(0, "UString::shrink()")
|
|
|
|
#ifdef ENABLE_MEMPOOL
|
|
uint32_t _length = rep->_length, sz = _length+(1+sizeof(UStringRep)); // NB: we need an array of char[_length], plus a terminating null char, plus the UStringRep data structure...
|
|
|
|
U_INTERNAL_DUMP("rep->_capacity = %u _length = %u sz = %u", rep->_capacity, _length, sz)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(rep->_capacity, 0) // mode: 0 -> const
|
|
|
|
if (sz <= U_STACK_TYPE_8) // 2048
|
|
{
|
|
int stack_index;
|
|
uint32_t _capacity;
|
|
|
|
if (sz <= U_STACK_TYPE_4) // 128
|
|
{
|
|
_capacity = U_STACK_TYPE_4-(1+sizeof(UStringRep));
|
|
stack_index = 4;
|
|
}
|
|
else if (sz <= U_STACK_TYPE_5) // 256
|
|
{
|
|
_capacity = U_STACK_TYPE_5-(1+sizeof(UStringRep));
|
|
stack_index = 5;
|
|
}
|
|
else if (sz <= U_STACK_TYPE_6) // 512
|
|
{
|
|
_capacity = U_STACK_TYPE_6-(1+sizeof(UStringRep));
|
|
stack_index = 6;
|
|
}
|
|
else if (sz <= U_STACK_TYPE_7) // 1024
|
|
{
|
|
_capacity = U_STACK_TYPE_7-(1+sizeof(UStringRep));
|
|
stack_index = 7;
|
|
}
|
|
else // 2048
|
|
{
|
|
_capacity = U_STACK_TYPE_8-(1+sizeof(UStringRep));
|
|
stack_index = 8;
|
|
}
|
|
|
|
U_INTERNAL_DUMP("_capacity = %u stack_index = %u", _capacity, stack_index)
|
|
|
|
if (_capacity < rep->_capacity)
|
|
{
|
|
UStringRep* r = (UStringRep*) UMemoryPool::pop(stack_index);
|
|
char* ptr = (char*)(r + 1);
|
|
|
|
# ifdef DEBUG
|
|
U_SET_LOCATION_INFO;
|
|
U_REGISTER_OBJECT_PTR(0,UStringRep,r,&(r->memory._this))
|
|
r->memory._this = (void*)U_CHECK_MEMORY_SENTINEL;
|
|
# endif
|
|
|
|
r->set(_length, _capacity, ptr);
|
|
|
|
U_MEMCPY((void*)ptr, rep->str, _length);
|
|
|
|
ptr[_length] = '\0';
|
|
|
|
U_INTERNAL_ASSERT(r->invariant())
|
|
|
|
_set(r);
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
|
|
U_RETURN(true);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
void UStringRep::release()
|
|
{
|
|
U_TRACE(0, "UStringRep::release()") // problem with sanitize address
|
|
|
|
U_INTERNAL_DUMP("this = %p parent = %p references = %u child = %d", this, parent, references, child)
|
|
|
|
#ifdef DEBUG
|
|
bool ok = memory.invariant();
|
|
|
|
if (ok == false)
|
|
{
|
|
U_ERROR("UStringRep::release() %s - this = %p parent = %p references = %u child = %d _capacity = %u str(%u) = %.*S",
|
|
memory.getErrorType(this),
|
|
this, parent, references, child, _capacity, _length, _length, str);
|
|
}
|
|
#endif
|
|
|
|
if (references)
|
|
{
|
|
--references;
|
|
|
|
return;
|
|
}
|
|
|
|
// NB: we don't use delete (dtor) because add a deallocation to the destroy process...
|
|
|
|
U_INTERNAL_DUMP("_capacity = %u str(%u) = %.*S", _capacity, _length, _length, str)
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(references, 0)
|
|
U_INTERNAL_ASSERT_DIFFERS(this, string_rep_null)
|
|
|
|
#if defined(U_SUBSTR_INC_REF) || defined(DEBUG)
|
|
if (parent)
|
|
# ifdef U_SUBSTR_INC_REF
|
|
parent->release(); // NB: solo la morte della substring de-referenzia la source...
|
|
# else
|
|
{
|
|
U_INTERNAL_ASSERT_EQUALS(child, 0)
|
|
|
|
U_INTERNAL_DUMP("parent->child = %d", parent->child)
|
|
|
|
// U_INTERNAL_ASSERT_RANGE(1, parent->child, max_child)
|
|
|
|
if (parent->child >= 1 &&
|
|
parent->child <= max_child)
|
|
{
|
|
parent->child--;
|
|
|
|
U_INTERNAL_DUMP("this = %p parent = %p parent->references = %u parent->child = %d", this, parent, parent->references, parent->child)
|
|
}
|
|
else
|
|
{
|
|
U_WARNING("parent->child has value(%d) out of range (1-%d)", parent->child, max_child);
|
|
}
|
|
}
|
|
else // source...
|
|
{
|
|
if (child)
|
|
{
|
|
if (UObjectDB::fd > 0)
|
|
{
|
|
parent_destroy = this;
|
|
|
|
U_DUMP_OBJECT("DEAD OF SOURCE STRING WITH CHILD ALIVE - child of this", checkIfChild)
|
|
}
|
|
else
|
|
{
|
|
char buffer[4096];
|
|
|
|
(void) u__snprintf(buffer, sizeof(buffer), "DEAD OF SOURCE STRING WITH CHILD ALIVE: child(%u) source(%u) = %.*S", child, _length, _length, str);
|
|
|
|
if (check_dead_of_source_string_with_child_alive)
|
|
{
|
|
U_INTERNAL_ASSERT_MSG(false, buffer)
|
|
}
|
|
else
|
|
{
|
|
U_WARNING("%s", buffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
# endif
|
|
# ifdef DEBUG
|
|
U_UNREGISTER_OBJECT(0, this)
|
|
# endif
|
|
#endif
|
|
|
|
#ifndef ENABLE_MEMPOOL
|
|
U_SYSCALL_VOID(free, "%p", (void*)this);
|
|
#else
|
|
if (_capacity <= U_CAPACITY)
|
|
{
|
|
if (_capacity == 0) UMemoryPool::push(this, U_SIZE_TO_STACK_INDEX(sizeof(UStringRep))); // NB: no room for data, which mean constant string...
|
|
else
|
|
{
|
|
// NB: we need an array of char[_capacity], plus a terminating null char element, plus enough for the UStringRep data structure...
|
|
|
|
uint32_t sz = _capacity + (1 + sizeof(UStringRep));
|
|
|
|
/**
|
|
* -----------
|
|
* power of 2:
|
|
* -----------
|
|
* 2^7 128
|
|
* 2^8 256
|
|
* 2^9 512
|
|
* 2^10 1024
|
|
* 2^11 2048
|
|
* 2^12 4096
|
|
* -----------
|
|
*/
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(sz & (sz-1), 0) // must be a power of 2
|
|
|
|
UMemoryPool::push(this, MultiplyDeBruijnBitPosition2[(sz * 0x077CB531U) >> 27] - 3);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_capacity != U_NOT_FOUND)
|
|
{
|
|
U_INTERNAL_ASSERT_EQUALS(_capacity & U_PAGEMASK, 0)
|
|
|
|
UMemoryPool::deallocate((void*)str, _capacity);
|
|
}
|
|
else
|
|
{
|
|
ptrdiff_t resto = (ptrdiff_t)str % PAGESIZE;
|
|
|
|
U_INTERNAL_DUMP("resto = %u _length = %u", resto, _length)
|
|
|
|
if (resto)
|
|
{
|
|
str -= resto;
|
|
_length += resto;
|
|
}
|
|
|
|
(void) U_SYSCALL(munmap, "%p,%lu", (void*)str, _length);
|
|
}
|
|
|
|
U_FREE_TYPE(this, UStringRep); // NB: in debug mode the memory area is zeroed...
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void UStringRep::fromValue(UStringRep* r)
|
|
{
|
|
U_TRACE(0, "UStringRep::fromValue(%V)", r)
|
|
|
|
U_INTERNAL_DUMP("r = %p r->parent = %p r->references = %d r->child = %d - %V", r, r->parent, r->references, r->child, r)
|
|
|
|
U_INTERNAL_ASSERT(r->_capacity)
|
|
U_INTERNAL_ASSERT_EQUALS(memcmp(this, UStringRep::string_rep_null, sizeof(UStringRep)), 0)
|
|
|
|
u__memcpy(this, r, sizeof(UStringRep), __PRETTY_FUNCTION__);
|
|
|
|
r->_capacity = 0; // NB: no room for data, constant string...
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// substring capture event 'DEAD OF SOURCE STRING WITH CHILD ALIVE'...
|
|
int32_t UStringRep::max_child;
|
|
UStringRep* UStringRep::parent_destroy;
|
|
UStringRep* UStringRep::string_rep_share;
|
|
bool UStringRep::check_dead_of_source_string_with_child_alive = true;
|
|
|
|
bool UStringRep::checkIfReferences(const char* name_class, const void* ptr_object)
|
|
{
|
|
U_TRACE(0, "UStringRep::checkIfReferences(%S,%p)", name_class, ptr_object)
|
|
|
|
if (strncmp(name_class, U_CONSTANT_TO_PARAM("UString")) == 0)
|
|
{
|
|
U_INTERNAL_DUMP("references = %u", ((UString*)ptr_object)->rep->references)
|
|
|
|
if (((UString*)ptr_object)->rep == string_rep_share) U_RETURN(true);
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
bool UStringRep::checkIfChild(const char* name_class, const void* ptr_object)
|
|
{
|
|
U_TRACE(0, "UStringRep::checkIfChild(%S,%p)", name_class, ptr_object)
|
|
|
|
if (strncmp(name_class, U_CONSTANT_TO_PARAM("UStringRep")) == 0)
|
|
{
|
|
U_INTERNAL_DUMP("parent = %p", ((UStringRep*)ptr_object)->parent)
|
|
|
|
if (((UStringRep*)ptr_object)->parent == parent_destroy) U_RETURN(true);
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
#endif
|
|
|
|
UStringRep* UStringRep::substr(const char* t, uint32_t tlen)
|
|
{
|
|
U_TRACE(0+256, "UStringRep::substr(%.*S,%u)", tlen, t, tlen)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
U_INTERNAL_ASSERT(tlen <= _length)
|
|
|
|
UStringRep* r;
|
|
|
|
if (tlen == 0)
|
|
{
|
|
r = string_rep_null;
|
|
|
|
r->references++;
|
|
}
|
|
else
|
|
{
|
|
U_INTERNAL_ASSERT_RANGE(str, t, end())
|
|
|
|
r = U_NEW(UStringRep(t, tlen));
|
|
|
|
# if defined(U_SUBSTR_INC_REF) || defined(DEBUG)
|
|
UStringRep* p = this;
|
|
|
|
while (p->parent)
|
|
{
|
|
p = p->parent;
|
|
|
|
U_INTERNAL_ASSERT(p->invariant())
|
|
}
|
|
|
|
r->parent = p;
|
|
|
|
# ifdef U_SUBSTR_INC_REF
|
|
p->references++; // substring increment reference of source string
|
|
# else
|
|
p->child++; // substring capture event 'DEAD OF SOURCE STRING WITH CHILD ALIVE'...
|
|
|
|
max_child = U_max(max_child, p->child);
|
|
# endif
|
|
|
|
U_INTERNAL_DUMP("r->parent = %p max_child = %d", r->parent, max_child)
|
|
# endif
|
|
}
|
|
|
|
U_RETURN_POINTER(r, UStringRep);
|
|
}
|
|
|
|
__pure bool UStringRep::isSubStringOf(UStringRep* rep) const
|
|
{
|
|
U_TRACE(0, "UStringRep::isSubStringOf(%V)", rep)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
if (this != rep &&
|
|
_capacity == 0 && // mode: 0 -> const
|
|
begin() >= rep->begin() &&
|
|
end() <= rep->end())
|
|
{
|
|
# if defined(U_SUBSTR_INC_REF) || defined(DEBUG)
|
|
U_INTERNAL_ASSERT_EQUALS(parent, rep)
|
|
U_INTERNAL_ASSERT_MAJOR(rep->child, 0)
|
|
# endif
|
|
|
|
U_RETURN(true);
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
void UStringRep::replace(const char* s, uint32_t n)
|
|
{
|
|
U_TRACE(0, "UStringRep::replace(%S,%u)", s, n)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(n, 0)
|
|
U_INTERNAL_ASSERT(_capacity >= n)
|
|
|
|
U_MEMCPY((char*)str, s, n);
|
|
|
|
((char*)str)[(_length = n)] = '\0';
|
|
}
|
|
|
|
void UStringRep::assign(UStringRep*& rep, const char* s, uint32_t n)
|
|
{
|
|
U_TRACE(0, "UStringRep::assign(%p,%S,%u)", rep, s, n)
|
|
|
|
if (rep->references ||
|
|
rep->_capacity < n)
|
|
{
|
|
rep->release();
|
|
|
|
rep = (n ? U_NEW(UStringRep(s, n)) : string_rep_null);
|
|
}
|
|
else
|
|
{
|
|
char* ptr = (char*)rep->str;
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(n, 0)
|
|
U_INTERNAL_ASSERT_DIFFERS(ptr, s)
|
|
|
|
U_MEMCPY(ptr, s, n);
|
|
|
|
U_ASSERT(rep->uniq())
|
|
|
|
ptr[(rep->_length = n)] = '\0';
|
|
}
|
|
}
|
|
|
|
void UStringRep::copy(char* s, uint32_t n, uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UStringRep::copy(%p,%u,%u)", s, n, pos)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
U_INTERNAL_ASSERT(pos <= _length)
|
|
|
|
if (n > (_length - pos)) n = (_length - pos);
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(n, 0)
|
|
|
|
U_MEMCPY(s, str + pos, n);
|
|
|
|
s[n] = '\0';
|
|
}
|
|
|
|
void UStringRep::trim()
|
|
{
|
|
U_TRACE(0, "UStringRep::trim()")
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(_capacity, 0)
|
|
|
|
// skip white space from start
|
|
|
|
while (_length && u__isspace(*str))
|
|
{
|
|
++str;
|
|
--_length;
|
|
}
|
|
|
|
U_INTERNAL_DUMP("_length = %u", _length)
|
|
|
|
// skip white space from end
|
|
|
|
while (_length && u__isspace(str[_length-1])) --_length;
|
|
}
|
|
|
|
__pure int UStringRep::compare(const UStringRep* rep, uint32_t depth) const
|
|
{
|
|
U_TRACE(0, "UStringRep::compare(%p,%u)", rep, depth)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
int r;
|
|
uint32_t min = U_min(_length, rep->_length);
|
|
|
|
U_INTERNAL_DUMP("min = %u", min)
|
|
|
|
if (depth > min) goto next;
|
|
|
|
r = memcmp(str + depth, rep->str + depth, min - depth);
|
|
|
|
U_INTERNAL_DUMP("str[%u] = %.*S", depth, min - depth, str + depth)
|
|
|
|
if (r == 0)
|
|
next:
|
|
r = (_length - rep->_length);
|
|
|
|
U_RETURN(r);
|
|
}
|
|
|
|
__pure bool UStringRep::equal(const char* s, uint32_t n) const
|
|
{
|
|
U_TRACE(0, "UStringRep::equal(%#.*S,%u)", n, s, n) // problem with sanitize address
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
|
|
if (_length == n &&
|
|
memcmp(str, s, n) == 0)
|
|
{
|
|
U_RETURN(true);
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
__pure bool UStringRep::equal(const char* s, uint32_t n, bool ignore_case) const
|
|
{
|
|
U_TRACE(0, "UStringRep::equal(%.*S,%u,%b)", n, s, n, ignore_case)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
|
|
if (_length == n)
|
|
{
|
|
bool r = ((ignore_case ? u__strncasecmp(str, s, n)
|
|
: memcmp(str, s, n)) == 0);
|
|
|
|
U_RETURN(r);
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
__pure bool UStringRep::isBase64(uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UStringRep::isBase64(%u)", pos)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
if (_length)
|
|
{
|
|
U_INTERNAL_ASSERT_MINOR(pos, _length)
|
|
|
|
bool result = u_isBase64(str + pos, _length - pos);
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
__pure bool UStringRep::isPrintable(uint32_t pos, bool bline) const
|
|
{
|
|
U_TRACE(0, "UStringRep::isPrintable(%u,%b)", pos, bline)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
if (_length)
|
|
{
|
|
U_INTERNAL_ASSERT_MINOR(pos, _length)
|
|
|
|
bool result = u_isPrintable(str + pos, _length - pos, bline);
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
__pure bool UStringRep::isWhiteSpace(uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UStringRep::isWhiteSpace(%u)", pos)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
if (_length)
|
|
{
|
|
U_INTERNAL_ASSERT_MINOR(pos, _length)
|
|
|
|
bool result = u_isWhiteSpace(str + pos, _length - pos);
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
U_RETURN(true);
|
|
}
|
|
|
|
__pure uint32_t UStringRep::findWhiteSpace(uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UStringRep::findWhiteSpace(%u)", pos)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
U_INTERNAL_ASSERT(pos <= _length)
|
|
|
|
for (; pos < _length; ++pos)
|
|
{
|
|
if (u__isspace(str[pos])) U_RETURN(pos);
|
|
}
|
|
|
|
U_RETURN(U_NOT_FOUND);
|
|
}
|
|
|
|
__pure bool UStringRep::isEndHeader(uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UStringRep::isEndHeader(%u)", pos)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
U_INTERNAL_ASSERT_MINOR(pos, _length)
|
|
|
|
const char* ptr = str + pos;
|
|
uint32_t _remain = (_length - pos);
|
|
|
|
if (_remain >= 4 &&
|
|
*(int32_t*)ptr == U_MULTICHAR_CONSTANT32('\r','\n','\r','\n'))
|
|
{
|
|
// U_line_terminator_len = 2;
|
|
|
|
U_INTERNAL_ASSERT(u__islterm(*ptr))
|
|
|
|
U_RETURN(true);
|
|
}
|
|
|
|
if (_remain >= 2 &&
|
|
*(int16_t*)ptr == U_MULTICHAR_CONSTANT16('\n','\n'))
|
|
{
|
|
// U_line_terminator_len = 1;
|
|
|
|
U_INTERNAL_ASSERT(u__islterm(*ptr))
|
|
|
|
U_RETURN(true);
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
__pure bool UStringRep::isText(uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UStringRep::isText(%u)", pos)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
if (_length)
|
|
{
|
|
U_INTERNAL_ASSERT_MINOR(pos, _length)
|
|
|
|
bool result = u_isText((const unsigned char*)(str + pos), _length - pos);
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
U_RETURN(true);
|
|
}
|
|
|
|
__pure bool UStringRep::isBinary(uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UStringRep::isBinary(%u)", pos)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
U_INTERNAL_ASSERT_MINOR(pos, _length)
|
|
|
|
bool result = u_isBinary((const unsigned char*)(str + pos), _length - pos);
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
__pure bool UStringRep::isUTF8(uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UStringRep::isUTF8(%u)", pos)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
U_INTERNAL_ASSERT_MINOR(pos, _length)
|
|
|
|
bool result = u_isUTF8((const unsigned char*)(str + pos), _length - pos);
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
__pure bool UStringRep::isUTF16(uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UStringRep::isUTF16(%u)", pos)
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
U_INTERNAL_ASSERT_MINOR(pos, _length)
|
|
|
|
bool result = u_isUTF16((const unsigned char*)(str + pos), _length - pos);
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
UString::UString(const char* t)
|
|
{
|
|
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%S", t)
|
|
|
|
uint32_t len = (t ? u__strlen(t, __PRETTY_FUNCTION__) : 0);
|
|
|
|
if (len) rep = U_NEW(UStringRep(t, len));
|
|
else _copy(UStringRep::string_rep_null);
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
UString::UString(const char* t, uint32_t len)
|
|
{
|
|
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%.*S,%u", len, t, len)
|
|
|
|
if (len) rep = U_NEW(UStringRep(t, len));
|
|
else _copy(UStringRep::string_rep_null);
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
UString::UString(const UString& str, uint32_t pos, uint32_t n)
|
|
{
|
|
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%p,%u,%u", &str, pos, n)
|
|
|
|
U_INTERNAL_ASSERT(pos <= str.size())
|
|
|
|
uint32_t sz = str.rep->fold(pos, n);
|
|
|
|
if (sz) rep = UStringRep::create(sz, sz, str.rep->str + pos);
|
|
else _copy(UStringRep::string_rep_null);
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
UString::UString(ustringrep* r)
|
|
{
|
|
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%p", r)
|
|
|
|
#ifdef DEBUG
|
|
r->_this = (void*)U_CHECK_MEMORY_SENTINEL;
|
|
#endif
|
|
|
|
uustringrep u = { r };
|
|
|
|
_copy(u.p2);
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
UString UString::copy() const
|
|
{
|
|
U_TRACE(0, "UString::copy()")
|
|
|
|
if (rep->_length)
|
|
{
|
|
uint32_t sz = rep->_length;
|
|
|
|
UString copia((void*)rep->str, sz);
|
|
|
|
U_RETURN_STRING(copia);
|
|
}
|
|
|
|
U_RETURN_STRING(getStringNull());
|
|
}
|
|
|
|
// SERVICES
|
|
|
|
UString::UString(uint32_t n, unsigned char c)
|
|
{
|
|
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%u,%C", n, c)
|
|
|
|
rep = UStringRep::create(n, n, 0);
|
|
|
|
(void) memset((void*)rep->str, c, n);
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
UString::UString(uint32_t len, uint32_t sz, char* ptr) // NB: for UStringExt::deflate()...
|
|
{
|
|
U_TRACE_REGISTER_OBJECT_WITHOUT_CHECK_MEMORY(0, UString, "%u,%u,%p", len, sz, ptr)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(sz, U_CAPACITY)
|
|
U_INTERNAL_ASSERT_EQUALS(sz & U_PAGEMASK, 0)
|
|
|
|
rep = U_MALLOC_TYPE(UStringRep);
|
|
|
|
#ifdef DEBUG
|
|
U_SET_LOCATION_INFO;
|
|
U_REGISTER_OBJECT_PTR(0,UStringRep,rep,&(rep->memory._this))
|
|
rep->memory._this = (void*)U_CHECK_MEMORY_SENTINEL;
|
|
#endif
|
|
|
|
rep->set(len, sz, ptr);
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
void UString::setBuffer(uint32_t n)
|
|
{
|
|
U_TRACE(0, "UString::setBuffer(%u)", n)
|
|
|
|
U_INTERNAL_ASSERT_RANGE(1, n, max_size())
|
|
|
|
U_INTERNAL_DUMP("rep = %p rep->parent = %p rep->references = %u rep->child = %d rep->_capacity = %u",
|
|
rep, rep->parent, rep->references, rep->child, rep->_capacity)
|
|
|
|
if (rep->references == 0 &&
|
|
n <= rep->_capacity)
|
|
{
|
|
((char*)rep->str)[(rep->_length = 0)] = '\0';
|
|
}
|
|
else
|
|
{
|
|
if (n < U_CAPACITY) n = U_CAPACITY;
|
|
|
|
_set(UStringRep::create(0U, n, 0));
|
|
}
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
void UString::moveToBeginDataInBuffer(uint32_t n)
|
|
{
|
|
U_TRACE(1, "UString::moveToBeginDataInBuffer(%u)", n)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(rep->_length, n)
|
|
U_INTERNAL_ASSERT_RANGE(1, n, max_size())
|
|
U_INTERNAL_ASSERT_MAJOR(rep->_capacity, n)
|
|
|
|
#if defined(DEBUG) && !defined(U_SUBSTR_INC_REF)
|
|
U_INTERNAL_ASSERT(rep->references == 0)
|
|
#endif
|
|
|
|
rep->_length -= n;
|
|
|
|
(void) U_SYSCALL(memmove, "%p,%p,%u", (void*)rep->str, rep->str + n, rep->_length);
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
void UString::_reserve(UString& buffer, uint32_t n)
|
|
{
|
|
U_TRACE(0, "UString::_reserve(%V,%u)", buffer.rep, n)
|
|
|
|
UStringRep* rep = buffer.rep;
|
|
|
|
U_INTERNAL_DUMP("rep = %p rep->parent = %p rep->references = %u rep->child = %d rep->_length = %u rep->_capacity = %u",
|
|
rep, rep->parent, rep->references, rep->child, rep->_length, rep->_capacity)
|
|
|
|
U_ASSERT(rep->space() < n)
|
|
U_INTERNAL_ASSERT(n <= max_size())
|
|
|
|
uint32_t need = rep->_length + n;
|
|
|
|
if (need < U_CAPACITY) need = U_CAPACITY;
|
|
else if (need > U_CAPACITY)
|
|
{
|
|
if (need < 2*1024*1024) need = (need * 2) + (PAGESIZE * 2);
|
|
need += PAGESIZE; // NB: to avoid duplication on realloc...
|
|
}
|
|
|
|
buffer._set(UStringRep::create(rep->_length, need, rep->str));
|
|
|
|
U_INTERNAL_ASSERT(buffer.invariant())
|
|
U_INTERNAL_ASSERT(buffer.space() >= n)
|
|
}
|
|
|
|
// manage UString as memory mapped area...
|
|
|
|
void UString::mmap(const char* map, uint32_t len)
|
|
{
|
|
U_TRACE(0, "UString::mmap(%.*S,%u)", len, map, len)
|
|
|
|
U_INTERNAL_ASSERT_DIFFERS(map, MAP_FAILED)
|
|
|
|
if (isMmap())
|
|
{
|
|
U_ASSERT(uniq())
|
|
|
|
rep->str = map;
|
|
rep->_length = len;
|
|
}
|
|
else
|
|
{
|
|
_set(U_NEW(UStringRep(map, len)));
|
|
|
|
rep->_capacity = U_NOT_FOUND;
|
|
|
|
# if defined(MADV_SEQUENTIAL)
|
|
if (len > (64 * PAGESIZE)) (void) U_SYSCALL(madvise, "%p,%u,%d", (void*)map, len, MADV_SEQUENTIAL);
|
|
# endif
|
|
}
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
U_NO_EXPORT char* UString::__replace(uint32_t pos, uint32_t n1, uint32_t n2)
|
|
{
|
|
U_TRACE(0, "UString::__replace(%u,%u,%u)", pos, n1, n2)
|
|
|
|
U_INTERNAL_ASSERT_DIFFERS(n2, U_NOT_FOUND)
|
|
|
|
uint32_t sz = size();
|
|
|
|
U_INTERNAL_ASSERT(pos <= sz)
|
|
|
|
uint32_t sz1 = rep->fold(pos, n1),
|
|
n = sz + n2 - sz1;
|
|
|
|
U_INTERNAL_DUMP("sz1 = %u, n = %u", sz1, n)
|
|
|
|
if (n == 0)
|
|
{
|
|
_assign(UStringRep::string_rep_null);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t how_much = sz - pos - sz1;
|
|
|
|
U_INTERNAL_DUMP("how_much = %d", how_much)
|
|
|
|
U_INTERNAL_ASSERT(how_much >= 0)
|
|
|
|
char* str = (char*)rep->str;
|
|
const char* src = str + pos + sz1;
|
|
|
|
uint32_t __capacity = rep->_capacity;
|
|
|
|
if (__capacity == U_NOT_FOUND) __capacity = 0;
|
|
|
|
if (rep->references ||
|
|
n > __capacity)
|
|
{
|
|
U_INTERNAL_DUMP("__capacity = %u, n = %u", __capacity, n)
|
|
|
|
if (__capacity < n) __capacity = n;
|
|
|
|
UStringRep* r = UStringRep::create(n, __capacity, 0);
|
|
|
|
if (pos) U_MEMCPY((void*)r->str, str, pos);
|
|
if (how_much) U_MEMCPY((char*)r->str + pos + n2, src, how_much);
|
|
|
|
_set(r);
|
|
|
|
str = (char*)r->str;
|
|
}
|
|
else if (how_much > 0 &&
|
|
n1 != n2)
|
|
{
|
|
(void) U_SYSCALL(memmove, "%p,%p,%u", str + pos + n2, src, how_much);
|
|
}
|
|
|
|
U_ASSERT(uniq())
|
|
|
|
str[(rep->_length = n)] = '\0';
|
|
|
|
return str + pos;
|
|
}
|
|
|
|
UString& UString::replace(uint32_t pos, uint32_t n1, const char* s, uint32_t n2)
|
|
{
|
|
U_TRACE(0, "UString::replace(%u,%u,%S,%u)", pos, n1, s, n2)
|
|
|
|
char* ptr = __replace(pos, n1, n2);
|
|
|
|
if (ptr && n2) U_MEMCPY(ptr, s, n2);
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
|
|
return *this;
|
|
}
|
|
|
|
UString& UString::replace(uint32_t pos, uint32_t n1, uint32_t n2, char c)
|
|
{
|
|
U_TRACE(0, "UString::replace(%u,%u,%u,%C)", pos, n1, n2, c)
|
|
|
|
char* ptr = __replace(pos, n1, n2);
|
|
|
|
if (ptr && n2) (void) U_SYSCALL(memset, "%p,%d,%u", ptr, c, n2);
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
|
|
return *this;
|
|
}
|
|
|
|
void UString::unQuote()
|
|
{
|
|
U_TRACE(0, "UString::unQuote()")
|
|
|
|
U_ASSERT(uniq())
|
|
|
|
uint32_t len = rep->_length;
|
|
|
|
if (len <= 2) clear();
|
|
else if (rep->_capacity == 0) rep->unQuote();
|
|
else
|
|
{
|
|
len -= 2;
|
|
|
|
char* ptr = (char*) rep->str;
|
|
|
|
(void) U_SYSCALL(memmove, "%p,%p,%u", ptr, ptr + 1, len);
|
|
|
|
ptr[(rep->_length = len)] = '\0';
|
|
}
|
|
}
|
|
|
|
U_NO_EXPORT char* UString::__append(uint32_t n)
|
|
{
|
|
U_TRACE(0, "UString::__append(%u)", n)
|
|
|
|
UStringRep* r;
|
|
char* str = (char*)rep->str;
|
|
uint32_t sz = rep->_length, need = sz + n;
|
|
|
|
U_INTERNAL_DUMP("need = %u", need)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(need, 0)
|
|
|
|
if (rep->references ||
|
|
need > rep->_capacity)
|
|
{
|
|
r = UStringRep::create(sz, (need < U_CAPACITY ? U_CAPACITY : (need * 2) + (PAGESIZE * 2)), str);
|
|
|
|
_set(r);
|
|
|
|
str = (char*)r->str;
|
|
}
|
|
|
|
U_ASSERT(uniq())
|
|
|
|
str[(rep->_length = need)] = '\0';
|
|
|
|
return str + sz;
|
|
}
|
|
|
|
UString& UString::append(const char* s, uint32_t n)
|
|
{
|
|
U_TRACE(0, "UString::append(%.*S,%u)", n, s, n) // problem with sanitize address
|
|
|
|
if (n)
|
|
{
|
|
char* ptr = __append(n);
|
|
|
|
U_MEMCPY(ptr, s, n);
|
|
}
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
|
|
return *this;
|
|
}
|
|
|
|
UString& UString::append(uint32_t n, char c)
|
|
{
|
|
U_TRACE(0, "UString::append(%u,%C)", n, c)
|
|
|
|
if (n)
|
|
{
|
|
char* ptr = __append(n);
|
|
ptr[0] = c;
|
|
|
|
if (--n) (void) U_SYSCALL(memset, "%p,%d,%u", ptr+1, c, n);
|
|
}
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
|
|
return *this;
|
|
}
|
|
|
|
void UString::duplicate() const
|
|
{
|
|
U_TRACE(0, "UString::duplicate()")
|
|
|
|
uint32_t sz = size();
|
|
|
|
if (sz) ((UString*)this)->_set(UStringRep::create(sz, sz, rep->str));
|
|
else
|
|
{
|
|
((UString*)this)->_set(UStringRep::create(0, 100U, 0));
|
|
|
|
*(((UString*)this)->UString::rep->begin()) = '\0';
|
|
}
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
U_INTERNAL_ASSERT(isNullTerminated())
|
|
}
|
|
|
|
void UString::setNullTerminated() const
|
|
{
|
|
U_TRACE(0, "UString::setNullTerminated()")
|
|
|
|
// A file is mapped in multiples of the page size. For a file that is not a multiple of the page size,
|
|
// the remaining memory is zeroed when mapped, and writes to that region are not written out to the file
|
|
|
|
if (writeable() == false ||
|
|
(isMmap() && (rep->_length % PAGESIZE) == 0))
|
|
{
|
|
duplicate();
|
|
}
|
|
else
|
|
{
|
|
rep->setNullTerminated();
|
|
}
|
|
|
|
U_ASSERT_EQUALS(u__strlen(rep->str, __PRETTY_FUNCTION__), rep->_length)
|
|
}
|
|
|
|
void UString::resize(uint32_t n, unsigned char c)
|
|
{
|
|
U_TRACE(0, "UString::resize(%u,%C)", n, c)
|
|
|
|
U_INTERNAL_ASSERT(n <= max_size())
|
|
|
|
uint32_t sz = size();
|
|
|
|
if (n > sz) (void) append(n - sz, c);
|
|
else if (n < sz) erase(n);
|
|
else size_adjust(n);
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
// The `find' function searches string for a specified string (possibly a single character) and returns
|
|
// its starting position. You can supply the parameter pos to specify the position where search must begin
|
|
|
|
__pure uint32_t UString::find(const char* s, uint32_t pos, uint32_t s_len, uint32_t how_much) const
|
|
{
|
|
U_TRACE(0, "UString::find(%S,%u,%u,%u)", s, pos, s_len, how_much)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(s_len, 0)
|
|
|
|
// An empty string consists of no characters, therefore it should be found at every point in a UString, except beyond the end...
|
|
// if (s_len == 0) U_RETURN(pos <= size() ? pos : U_NOT_FOUND);
|
|
|
|
uint32_t n = rep->fold(pos, how_much);
|
|
|
|
U_INTERNAL_DUMP("rep->_length = %u", rep->_length)
|
|
|
|
U_INTERNAL_ASSERT(n <= rep->_length)
|
|
|
|
const char* str = rep->str;
|
|
const char* ptr = (const char*) u_find(str + pos, n, s, s_len);
|
|
|
|
n = (ptr ? ptr - str : U_NOT_FOUND);
|
|
|
|
U_RETURN(n);
|
|
}
|
|
|
|
__pure uint32_t UString::findnocase(const char* s, uint32_t pos, uint32_t s_len, uint32_t how_much) const
|
|
{
|
|
U_TRACE(0, "UString::findnocase(%S,%u,%u,%u)", s, pos, s_len, how_much)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(s_len, 1)
|
|
|
|
uint32_t n = rep->fold(pos, how_much);
|
|
int32_t __end = n - s_len + 1;
|
|
|
|
if (__end > 0)
|
|
{
|
|
const char* str = rep->str + pos;
|
|
|
|
for (int32_t xpos = 0; xpos < __end; ++xpos)
|
|
{
|
|
if (u__strncasecmp(str + xpos, s, s_len) == 0) U_RETURN(pos+xpos);
|
|
}
|
|
}
|
|
|
|
U_RETURN(U_NOT_FOUND);
|
|
}
|
|
|
|
__pure uint32_t UString::find(unsigned char c, uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UString::find(%C,%u)", c, pos)
|
|
|
|
uint32_t sz = size(),
|
|
ret = U_NOT_FOUND;
|
|
|
|
const char* str = rep->str;
|
|
|
|
if (pos < sz)
|
|
{
|
|
uint32_t how_much = (sz - pos);
|
|
|
|
// U_INTERNAL_DUMP("how_much = %u", how_much)
|
|
|
|
void* p = (void*) memchr(str + pos, c, how_much);
|
|
|
|
if (p) ret = (const char*)p - str;
|
|
}
|
|
|
|
U_RETURN(ret);
|
|
}
|
|
|
|
// rfind() instead of starting at the beginning of the string and searching for the text's first occurence, starts its search at the end and returns the last occurence
|
|
|
|
__pure uint32_t UString::rfind(unsigned char c, uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UString::rfind(%C,%u)", c, pos)
|
|
|
|
uint32_t sz = size();
|
|
|
|
if (sz)
|
|
{
|
|
uint32_t xpos = sz - 1;
|
|
|
|
if (xpos > pos) xpos = pos;
|
|
|
|
const char* str = rep->str;
|
|
|
|
for (++xpos; xpos-- > 0; )
|
|
{
|
|
if (str[xpos] == c) U_RETURN(xpos);
|
|
}
|
|
}
|
|
|
|
U_RETURN(U_NOT_FOUND);
|
|
}
|
|
|
|
__pure uint32_t UString::rfind(const char* s, uint32_t pos, uint32_t n) const
|
|
{
|
|
U_TRACE(0, "UString::rfind(%S,%u,%u)", s, pos, n)
|
|
|
|
uint32_t sz = size();
|
|
|
|
if (n <= sz)
|
|
{
|
|
pos = U_min(sz - n, pos);
|
|
|
|
const char* str = rep->str;
|
|
|
|
do {
|
|
if (memcmp(str + pos, s, n) == 0) U_RETURN(pos);
|
|
}
|
|
while (pos-- > 0);
|
|
}
|
|
|
|
U_RETURN(U_NOT_FOUND);
|
|
}
|
|
|
|
// Instead of searching for the entire string, find_first_of() returns as soon as a single common element is found between the strings being compared.
|
|
// And yes, this means that the find_first_of() that take a single char are exactly the same as the find() functions with the same parameters...
|
|
|
|
__pure uint32_t UString::find_first_of(const char* s, uint32_t pos, uint32_t n) const
|
|
{
|
|
U_TRACE(0, "UString::find_first_of(%S,%u,%u)", s, pos, n)
|
|
|
|
if (n)
|
|
{
|
|
uint32_t sz = size();
|
|
|
|
const char* str = rep->str;
|
|
|
|
for (; pos < sz; ++pos)
|
|
{
|
|
if (memchr(s, str[pos], n)) U_RETURN(pos);
|
|
}
|
|
}
|
|
|
|
U_RETURN(U_NOT_FOUND);
|
|
}
|
|
|
|
__pure uint32_t UString::find_last_of(const char* s, uint32_t pos, uint32_t n) const
|
|
{
|
|
U_TRACE(0, "UString::find_last_of(%S,%u,%u)", s, pos, n)
|
|
|
|
uint32_t sz = size();
|
|
|
|
if (sz && n)
|
|
{
|
|
if (--sz > pos) sz = pos;
|
|
|
|
const char* str = rep->str;
|
|
|
|
do {
|
|
if (memchr(s, str[sz], n)) U_RETURN(sz);
|
|
}
|
|
while (sz-- != 0);
|
|
}
|
|
|
|
U_RETURN(U_NOT_FOUND);
|
|
}
|
|
|
|
// Now these functions, instead of returning an index to the first common element, returns an index to the first non-common element...
|
|
|
|
__pure uint32_t UString::find_first_not_of(const char* s, uint32_t pos, uint32_t n) const
|
|
{
|
|
U_TRACE(0, "UString::find_first_not_of(%S,%u,%u)", s, pos, n)
|
|
|
|
if (n)
|
|
{
|
|
uint32_t sz = size(),
|
|
xpos = pos;
|
|
|
|
const char* str = rep->str;
|
|
|
|
for (; xpos < sz; ++xpos)
|
|
{
|
|
if (memchr(s, str[xpos], n) == 0) U_RETURN(xpos);
|
|
}
|
|
}
|
|
|
|
U_RETURN(U_NOT_FOUND);
|
|
}
|
|
|
|
__pure uint32_t UString::find_first_not_of(unsigned char c, uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UString::find_first_not_of(%C,%u)", c, pos)
|
|
|
|
uint32_t sz = size(),
|
|
xpos = pos;
|
|
|
|
const char* str = rep->str;
|
|
|
|
for (; xpos < sz; ++xpos)
|
|
{
|
|
if (str[xpos] != c) U_RETURN(xpos);
|
|
}
|
|
|
|
U_RETURN(U_NOT_FOUND);
|
|
}
|
|
|
|
__pure uint32_t UString::find_last_not_of(const char* s, uint32_t pos, uint32_t n) const
|
|
{
|
|
U_TRACE(0, "UString::find_last_not_of(%S,%u,%u)", s, pos, n)
|
|
|
|
uint32_t sz = size();
|
|
|
|
if (sz && n)
|
|
{
|
|
if (--sz > pos) sz = pos;
|
|
|
|
const char* str = rep->str;
|
|
|
|
do {
|
|
if (memchr(s, str[sz], n) == 0) U_RETURN(sz);
|
|
}
|
|
while (sz-- != 0);
|
|
}
|
|
|
|
U_RETURN(U_NOT_FOUND);
|
|
}
|
|
|
|
__pure uint32_t UString::find_last_not_of(unsigned char c, uint32_t pos) const
|
|
{
|
|
U_TRACE(0, "UString::find_last_not_of(%C,%u)", c, pos)
|
|
|
|
uint32_t sz = size();
|
|
|
|
if (sz)
|
|
{
|
|
uint32_t xpos = sz - 1;
|
|
|
|
if (xpos > pos) xpos = pos;
|
|
|
|
const char* str = rep->str;
|
|
|
|
for (++xpos; xpos-- > 0; )
|
|
{
|
|
if (str[xpos] != c) U_RETURN(xpos);
|
|
}
|
|
}
|
|
|
|
U_RETURN(U_NOT_FOUND);
|
|
}
|
|
|
|
// EXTENSION
|
|
|
|
void UString::snprintf(const char* format, ...)
|
|
{
|
|
U_TRACE(0, "UString::snprintf(%S)", format)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(format)
|
|
|
|
va_list argp;
|
|
va_start(argp, format);
|
|
|
|
UString::vsnprintf(format, argp);
|
|
|
|
va_end(argp);
|
|
}
|
|
|
|
void UString::snprintf_add(const char* format, ...)
|
|
{
|
|
U_TRACE(0, "UString::snprintf_add(%S)", format)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(format)
|
|
|
|
va_list argp;
|
|
va_start(argp, format);
|
|
|
|
UString::vsnprintf_add(format, argp);
|
|
|
|
va_end(argp);
|
|
}
|
|
|
|
__pure bool UStringRep::strtob() const
|
|
{
|
|
U_TRACE(0, "UStringRep::strtob()")
|
|
|
|
if (_length)
|
|
{
|
|
enum {
|
|
VAL_yes = U_MULTICHAR_CONSTANT32('y','e','s',0),
|
|
VAL_Yes = U_MULTICHAR_CONSTANT32('Y','e','s',0),
|
|
VAL_YES = U_MULTICHAR_CONSTANT32('Y','E','S',0),
|
|
VAL_true = U_MULTICHAR_CONSTANT32('t','r','u','e'),
|
|
VAL_True = U_MULTICHAR_CONSTANT32('T','r','u','e'),
|
|
VAL_TRUE = U_MULTICHAR_CONSTANT32('T','R','U','E')
|
|
};
|
|
|
|
switch (*(int32_t*)str)
|
|
{
|
|
case VAL_yes:
|
|
case VAL_Yes:
|
|
case VAL_YES:
|
|
case VAL_true:
|
|
case VAL_True:
|
|
case VAL_TRUE: U_RETURN(true);
|
|
}
|
|
|
|
switch (*(int16_t*)str)
|
|
{
|
|
case U_MULTICHAR_CONSTANT16('1',0):
|
|
case U_MULTICHAR_CONSTANT16('o','n'):
|
|
case U_MULTICHAR_CONSTANT16('O','n'):
|
|
case U_MULTICHAR_CONSTANT16('O','N'): U_RETURN(true);
|
|
}
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
long UStringRep::strtol(int base) const
|
|
{
|
|
U_TRACE(0, "UStringRep::strtol(%d)", base)
|
|
|
|
if (_length)
|
|
{
|
|
char* eos = (char*)str + _length;
|
|
|
|
if (isNullTerminated() == false && writeable()) *eos = '\0';
|
|
|
|
errno = 0;
|
|
|
|
char* endptr;
|
|
long result = (long) strtoul(str, &endptr, base);
|
|
|
|
U_INTERNAL_DUMP("errno = %d", errno)
|
|
|
|
if (endptr &&
|
|
endptr < eos)
|
|
{
|
|
U_NUMBER_SUFFIX(result, *endptr);
|
|
}
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
U_RETURN(0L);
|
|
}
|
|
|
|
#ifdef HAVE_STRTOULL
|
|
int64_t UStringRep::strtoll(int base) const
|
|
{
|
|
U_TRACE(0, "UStringRep::strtoll(%d)", base)
|
|
|
|
if (_length)
|
|
{
|
|
char* eos = (char*)str + _length;
|
|
|
|
if (isNullTerminated() == false && writeable()) *eos = '\0';
|
|
|
|
char* endptr;
|
|
int64_t result = (int64_t) strtoull(str, &endptr, base);
|
|
|
|
U_INTERNAL_DUMP("errno = %d", errno)
|
|
|
|
if (endptr &&
|
|
endptr < eos)
|
|
{
|
|
U_NUMBER_SUFFIX(result, *endptr);
|
|
}
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
U_RETURN(0LL);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_STRTOF
|
|
// extern "C" { float strtof(const char* nptr, char** endptr); }
|
|
float UStringRep::strtof() const
|
|
{
|
|
U_TRACE(0, "UStringRep::strtof()")
|
|
|
|
if (_length)
|
|
{
|
|
if (isNullTerminated() == false && writeable()) setNullTerminated();
|
|
|
|
float result = ::strtof(str, 0);
|
|
|
|
U_INTERNAL_DUMP("errno = %d", errno)
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
U_RETURN(0);
|
|
}
|
|
#endif
|
|
|
|
double UStringRep::strtod() const
|
|
{
|
|
U_TRACE(0, "UStringRep::strtod()")
|
|
|
|
if (_length)
|
|
{
|
|
if (isNullTerminated() == false && writeable()) setNullTerminated();
|
|
|
|
double result = ::strtod(str, 0);
|
|
|
|
U_INTERNAL_DUMP("errno = %d", errno)
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
U_RETURN(0);
|
|
}
|
|
|
|
#ifdef HAVE_STRTOLD
|
|
// extern "C" { long double strtold(const char* nptr, char** endptr); }
|
|
long double UStringRep::strtold() const
|
|
{
|
|
U_TRACE(0, "UStringRep::strtold()")
|
|
|
|
if (_length)
|
|
{
|
|
if (isNullTerminated() == false && writeable()) setNullTerminated();
|
|
|
|
long double result = ::strtold(str, 0);
|
|
|
|
U_INTERNAL_DUMP("errno = %d", errno)
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
U_RETURN(0);
|
|
}
|
|
#endif
|
|
|
|
// UTF8 <--> ISO Latin 1
|
|
|
|
UStringRep* UStringRep::fromUTF8(const unsigned char* s, uint32_t n)
|
|
{
|
|
U_TRACE(0, "UStringRep::fromUTF8(%.*S,%u)", n, s, n)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
|
|
if (n == 0) return UStringRep::string_rep_null;
|
|
|
|
int c, c1, c2;
|
|
UStringRep* r = UStringRep::create(n, n, 0);
|
|
|
|
char* p = (char*)r->str;
|
|
const unsigned char* _end = s + n;
|
|
|
|
while (s < _end)
|
|
{
|
|
if ( s < (_end - 1) &&
|
|
(*s & 0xE0) == 0xC0 &&
|
|
(*(s+1) & 0xC0) == 0x80)
|
|
{
|
|
c1 = *s++ & 0x1F;
|
|
c2 = *s++ & 0x3F;
|
|
c = (c1 << 6) + c2;
|
|
|
|
U_INTERNAL_DUMP("c = %d %C", c, (char)c)
|
|
}
|
|
else
|
|
{
|
|
c = *s++;
|
|
}
|
|
|
|
*p++ = (unsigned char)c;
|
|
}
|
|
|
|
r->_length = p - r->str;
|
|
|
|
U_INTERNAL_ASSERT(r->invariant())
|
|
|
|
U_RETURN_POINTER(r, UStringRep);
|
|
}
|
|
|
|
UStringRep* UStringRep::toUTF8(const unsigned char* s, uint32_t n)
|
|
{
|
|
U_TRACE(0, "UStringRep::toUTF8(%.*S,%u)", n, s, n)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
|
|
if (n == 0) return UStringRep::string_rep_null;
|
|
|
|
UStringRep* r = UStringRep::create(n, n * 2, 0);
|
|
|
|
char* p = (char*)r->str;
|
|
const unsigned char* _end = s + n;
|
|
|
|
while (s < _end)
|
|
{
|
|
int c = *s++;
|
|
|
|
if (c >= 0x80)
|
|
{
|
|
*p++ = ((c >> 6) & 0x1F) | 0xC0;
|
|
*p++ = ( c & 0x3F) | 0x80;
|
|
|
|
continue;
|
|
}
|
|
|
|
*p++ = (unsigned char)c;
|
|
}
|
|
|
|
r->_length = p - r->str;
|
|
|
|
U_INTERNAL_ASSERT(r->invariant())
|
|
|
|
U_RETURN_POINTER(r, UStringRep);
|
|
}
|
|
|
|
void UString::setFromData(const char** p, uint32_t sz)
|
|
{
|
|
U_TRACE(0, "UString::setFromData(%.*S,%u)", sz, *p, sz)
|
|
|
|
U_ASSERT(empty())
|
|
U_INTERNAL_ASSERT_MAJOR(sz, 0)
|
|
|
|
const char* ptr = *p;
|
|
unsigned char c = *ptr;
|
|
const char* pend = ptr + sz;
|
|
|
|
U_INTERNAL_DUMP("c = %C", c)
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(u__isspace(c), false)
|
|
|
|
if (LIKELY(c != '@'))
|
|
{
|
|
do {
|
|
_append(c);
|
|
|
|
if (++ptr >= pend) break;
|
|
|
|
c = *ptr;
|
|
|
|
if (u__isspace(c)) break;
|
|
}
|
|
while (true);
|
|
|
|
_append();
|
|
}
|
|
else
|
|
{
|
|
// get content pointed by string 'meta' (that start with '@')
|
|
|
|
if (*(int32_t*)(ptr+1) == U_MULTICHAR_CONSTANT32('F','I','L','E'))
|
|
{
|
|
UFile file;
|
|
char pathname[U_PATH_MAX];
|
|
|
|
ptr += U_CONSTANT_SIZE("@FILE:");
|
|
|
|
if (*ptr == '"') ++ptr; // check if string is quoted...
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(u__isspace(*ptr), false)
|
|
|
|
for (char* path = pathname; ptr < pend; ++path, ++ptr)
|
|
{
|
|
c = *ptr;
|
|
|
|
if (c == '"' ||
|
|
u__isspace(c))
|
|
{
|
|
if (c == '"') ++ptr;
|
|
|
|
*path = '\0';
|
|
|
|
break;
|
|
}
|
|
|
|
*path = c;
|
|
}
|
|
|
|
*p = ptr;
|
|
|
|
if (file.open(pathname)) *this = file.getContent();
|
|
else
|
|
{
|
|
U_WARNING("open file %S specified in configuration failed", pathname);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(memcmp(ptr, U_CONSTANT_TO_PARAM("@STRING:")), 0)
|
|
|
|
ptr += U_CONSTANT_SIZE("@STRING:");
|
|
|
|
const char* start;
|
|
|
|
if (*ptr == '"') // check if string is quoted...
|
|
{
|
|
ptr = u_find_char((start = (ptr+1)), pend, '"'); // find char '"' not quoted
|
|
|
|
if (ptr == pend)
|
|
{
|
|
(void) append(ptr, pend-ptr);
|
|
|
|
*p = pend;
|
|
|
|
return;
|
|
}
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(*ptr, '"')
|
|
|
|
sz = ptr++ - start;
|
|
}
|
|
else
|
|
{
|
|
for (start = ptr; ptr < pend; ++ptr)
|
|
{
|
|
c = *ptr;
|
|
|
|
if (u__isspace(c)) break;
|
|
}
|
|
|
|
sz = ptr - start;
|
|
}
|
|
|
|
setBuffer(sz * 4);
|
|
|
|
UEscape::decode(start, sz, *this);
|
|
}
|
|
|
|
*p = ptr;
|
|
|
|
if (empty() == false &&
|
|
shrink() == false)
|
|
{
|
|
setNullTerminated();
|
|
}
|
|
|
|
U_INTERNAL_DUMP("size = %u, str = %V", size(), rep)
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
void UString::setFromData(const char** p, uint32_t sz, unsigned char delim)
|
|
{
|
|
U_TRACE(0, "UString::setFromData(%.*S,%u,%C)", sz, *p, sz, delim)
|
|
|
|
U_ASSERT(empty())
|
|
|
|
const char* ptr = *p;
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(u__isspace(*ptr), false)
|
|
|
|
for (const char* pend = ptr + sz; ptr < pend; ++ptr)
|
|
{
|
|
unsigned char c = *ptr;
|
|
|
|
U_INTERNAL_DUMP("c = %C", c)
|
|
|
|
if (c == delim)
|
|
{
|
|
++ptr;
|
|
|
|
break;
|
|
}
|
|
|
|
if (c == '\\')
|
|
{
|
|
c = *++ptr;
|
|
|
|
U_INTERNAL_DUMP("c (after '\\') = %C", c)
|
|
|
|
if (c != delim)
|
|
{
|
|
if (c == '\n')
|
|
{
|
|
// compress multiple white-space in a single new-line...
|
|
|
|
U_INTERNAL_DUMP("ptr+1 = %.*S", 20, ptr+1)
|
|
|
|
while (ptr < pend)
|
|
{
|
|
if (u__isspace(ptr[1]) == false) break;
|
|
|
|
++ptr;
|
|
}
|
|
|
|
U_INTERNAL_DUMP("ptr+1 = %.*S", 20, ptr+1)
|
|
}
|
|
else if (strchr("nrtbfvae", c))
|
|
{
|
|
_append('\\');
|
|
}
|
|
}
|
|
}
|
|
|
|
_append(c);
|
|
}
|
|
|
|
*p = ptr;
|
|
|
|
_append();
|
|
|
|
if (empty() == false &&
|
|
shrink() == false)
|
|
{
|
|
setNullTerminated();
|
|
}
|
|
|
|
U_INTERNAL_DUMP("size = %u, str = %V", size(), rep)
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
}
|
|
|
|
// STREAM
|
|
|
|
#ifdef U_STDCPP_ENABLE
|
|
void UString::get(istream& is)
|
|
{
|
|
U_TRACE(0, "UString::get(%p)", &is) // problem with sanitize address
|
|
|
|
if (is.peek() != '"') is >> *this;
|
|
else
|
|
{
|
|
(void) is.get(); // skip '"'
|
|
|
|
(void) getline(is, '"');
|
|
}
|
|
}
|
|
|
|
void UStringRep::write(ostream& os) const
|
|
{
|
|
U_TRACE(0, "UStringRep::write(%p)", &os)
|
|
|
|
bool need_quote = (_length == 0);
|
|
|
|
if (need_quote == false)
|
|
{
|
|
for (const unsigned char* s = (const unsigned char*)str, *_end = s + _length; s < _end; ++s)
|
|
{
|
|
unsigned char c = *s;
|
|
|
|
if (c == '"' ||
|
|
c == '\\' ||
|
|
u__isspace(c))
|
|
{
|
|
need_quote = true;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (need_quote == false) os.write(str, _length);
|
|
else
|
|
{
|
|
os.put('"');
|
|
|
|
char* p;
|
|
char* s = (char*)str;
|
|
char* _end = s + _length;
|
|
|
|
while (s < _end)
|
|
{
|
|
p = (char*) memchr(s, '"', _end - s);
|
|
|
|
if (p == 0)
|
|
{
|
|
os.write(s, _end - s);
|
|
|
|
break;
|
|
}
|
|
|
|
os.write(s, p - s);
|
|
|
|
if (*(p-1) == '\\') os.put('\\');
|
|
os.put('\\');
|
|
os.put('"');
|
|
|
|
s = p + 1;
|
|
}
|
|
|
|
os.put('"');
|
|
}
|
|
}
|
|
|
|
U_EXPORT istream& operator>>(istream& in, UString& str)
|
|
{
|
|
U_TRACE(0+256, "UString::operator>>(%p,%p)", &in, &str)
|
|
|
|
uint32_t extracted = 0;
|
|
|
|
if (in.good())
|
|
{
|
|
streambuf* sb = in.rdbuf();
|
|
|
|
int c = sb->sbumpc();
|
|
|
|
if (in.flags() & ios::skipws)
|
|
{
|
|
while ((c != EOF) &&
|
|
u__isspace(c))
|
|
{
|
|
c = sb->sbumpc();
|
|
}
|
|
}
|
|
|
|
if (c != EOF)
|
|
{
|
|
if (str) str.rep->size_adjust(0U);
|
|
|
|
streamsize w = in.width();
|
|
|
|
uint32_t n = (w > 0 ? (uint32_t)w
|
|
: str.max_size());
|
|
|
|
while (extracted < n &&
|
|
u__isspace(c) == false)
|
|
{
|
|
str._append(c);
|
|
|
|
++extracted;
|
|
|
|
c = sb->sbumpc();
|
|
|
|
// U_INTERNAL_DUMP("c = %C, EOF = %C", c, EOF)
|
|
|
|
if (c == EOF) break;
|
|
}
|
|
|
|
str._append();
|
|
}
|
|
|
|
if (c == EOF) in.setstate(ios::eofbit);
|
|
else sb->sputbackc(c);
|
|
|
|
in.width(0);
|
|
|
|
U_INTERNAL_DUMP("size = %u, str = %V", str.size(), str.rep)
|
|
}
|
|
|
|
if (extracted == 0) in.setstate(ios::failbit);
|
|
else if (str.shrink() == false) str.setNullTerminated();
|
|
|
|
U_INTERNAL_ASSERT(str.invariant())
|
|
|
|
return in;
|
|
}
|
|
|
|
istream& UString::getline(istream& in, unsigned char delim)
|
|
{
|
|
U_TRACE(0+256, "UString::getline(%p,%C)", &in, delim)
|
|
|
|
int c = EOF;
|
|
bool extracted = false;
|
|
|
|
if (in.good())
|
|
{
|
|
if (empty() == false) rep->size_adjust(0U);
|
|
|
|
streambuf* sb = in.rdbuf();
|
|
|
|
while (true)
|
|
{
|
|
c = sb->sbumpc();
|
|
|
|
U_INTERNAL_DUMP("c = %C", c)
|
|
|
|
if (c == '\\')
|
|
{
|
|
c = sb->sbumpc();
|
|
|
|
U_INTERNAL_DUMP("c = %C", c)
|
|
|
|
if (c == delim)
|
|
{
|
|
_append(delim);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (strchr("nrt\nbfvae", c))
|
|
{
|
|
if (c != '\n') _append('\\');
|
|
else
|
|
{
|
|
// compress multiple white-space in a single new-line...
|
|
|
|
do { c = sb->sbumpc(); } while (c != EOF && u__isspace(c));
|
|
|
|
if (c != EOF)
|
|
{
|
|
sb->sputbackc(c);
|
|
|
|
c = '\n';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (c == EOF)
|
|
{
|
|
in.setstate(ios::eofbit);
|
|
|
|
break;
|
|
}
|
|
|
|
if (c == delim) break;
|
|
|
|
_append(c);
|
|
}
|
|
|
|
_append();
|
|
|
|
U_INTERNAL_DUMP("size = %u, str = %V", size(), rep)
|
|
|
|
extracted = (empty() == false);
|
|
|
|
if (extracted &&
|
|
shrink() == false)
|
|
{
|
|
setNullTerminated();
|
|
}
|
|
}
|
|
|
|
if (c != delim &&
|
|
extracted == false)
|
|
{
|
|
in.setstate(ios::failbit);
|
|
}
|
|
|
|
U_INTERNAL_ASSERT(invariant())
|
|
|
|
return in;
|
|
}
|
|
|
|
U_EXPORT ostream& operator<<(ostream& out, const UString& str)
|
|
{
|
|
U_TRACE(0, "UString::operator<<(%p,%p)", &out, &str)
|
|
|
|
if (out.good())
|
|
{
|
|
const char* s = str.data();
|
|
|
|
streamsize res,
|
|
w = out.width(),
|
|
len = (streamsize)str.size();
|
|
|
|
if (w <= len) res = out.rdbuf()->sputn(s, len);
|
|
else
|
|
{
|
|
int plen = (int)(w - len);
|
|
|
|
ios::fmtflags fmt = (out.flags() & ios::adjustfield);
|
|
|
|
if (fmt == ios::left)
|
|
{
|
|
res = out.rdbuf()->sputn(s, len);
|
|
|
|
// Padding last
|
|
|
|
for (int i = 0; i < plen; ++i) (void) out.rdbuf()->sputc(' ');
|
|
}
|
|
else
|
|
{
|
|
// Padding first
|
|
|
|
for (int i = 0; i < plen; ++i) (void) out.rdbuf()->sputc(' ');
|
|
|
|
res = out.rdbuf()->sputn(s, len);
|
|
}
|
|
}
|
|
|
|
U_INTERNAL_DUMP("len = %u, res = %u, w = %u", len, res, w)
|
|
|
|
out.width(0);
|
|
|
|
if (res != len) out.setstate(ios::failbit);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
#endif
|
|
|
|
// operator +
|
|
|
|
U_EXPORT UString operator+(const char* lhs, const UString& rhs)
|
|
{
|
|
uint32_t len = u__strlen(lhs, __PRETTY_FUNCTION__);
|
|
UString str(len + rhs.size());
|
|
|
|
(void) str.append(lhs, len);
|
|
(void) str.append(rhs);
|
|
|
|
return str;
|
|
}
|
|
|
|
U_EXPORT UString operator+(char lhs, const UString& rhs)
|
|
{
|
|
UString str(1U + rhs.size());
|
|
|
|
(void) str.append(1U, lhs);
|
|
(void) str.append(rhs);
|
|
|
|
return str;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
const char* UStringRep::dump(bool reset) const
|
|
{
|
|
#ifdef U_STDCPP_ENABLE
|
|
*UObjectIO::os << "length " << _length << '\n'
|
|
<< "capacity " << _capacity << '\n'
|
|
<< "references " << references << '\n'
|
|
<< "parent " << (void*)parent << '\n'
|
|
<< "child " << child << '\n'
|
|
<< "str " << (void*)str << ' ';
|
|
|
|
char buffer[1024];
|
|
|
|
UObjectIO::os->write(buffer, u__snprintf(buffer, sizeof(buffer), "%V", this));
|
|
|
|
if (reset)
|
|
{
|
|
UObjectIO::output();
|
|
|
|
return UObjectIO::buffer_output;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool UStringRep::invariant() const
|
|
{
|
|
if (_capacity &&
|
|
_capacity < _length)
|
|
{
|
|
U_WARNING("error on rep string: (overflow)\n"
|
|
"--------------------------------------------------\n%s", UStringRep::dump(true));
|
|
|
|
return false;
|
|
}
|
|
|
|
if ((int32_t)references < 0)
|
|
{
|
|
U_WARNING("error on rep string: (leak reference)\n"
|
|
"--------------------------------------------------\n%s", UStringRep::dump(true));
|
|
|
|
return false;
|
|
}
|
|
|
|
if (this == string_rep_null)
|
|
{
|
|
U_CHECK_MEMORY
|
|
|
|
if (_length)
|
|
{
|
|
U_WARNING("error on string_rep_null: (not empty)\n"
|
|
"--------------------------------------------------\n%s", UStringRep::dump(true));
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return string_rep_null->invariant();
|
|
}
|
|
|
|
bool UString::invariant() const
|
|
{
|
|
if (rep == 0)
|
|
{
|
|
U_WARNING("error on string: (rep = null pointer)");
|
|
|
|
return false;
|
|
}
|
|
|
|
return rep->invariant();
|
|
}
|
|
|
|
void UString::vsnprintf_check(const char* format) const
|
|
{
|
|
bool ok_writeable = writeable(),
|
|
ok_isNull = (isNull() == false),
|
|
ok_references = (rep->references == 0),
|
|
ok_format = (rep->_capacity > u__strlen(format, __PRETTY_FUNCTION__));
|
|
|
|
if (ok_writeable == false ||
|
|
ok_isNull == false ||
|
|
ok_format == false)
|
|
{
|
|
// -----------------------------------------------------------------------------------------------------------------------------------------
|
|
// Ex: userver_tcp: ERROR: UString::vsnprintf_check() this = 0xa79bbd18 parent = (nil) references = 2126 child = 0 _capacity = 0 str(0) = ""
|
|
// format = "%v:" - ok_writeable = false ok_isNull = false ok_references = false ok_format = false
|
|
// -----------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
U_ERROR("UString::vsnprintf_check() this = %p parent = %p references = %u child = %d _capacity = %u str(%u) = %V format = %S - "
|
|
"ok_writeable = %b ok_isNull = %b ok_references = %b ok_format = %b",
|
|
this, rep->parent, rep->references, rep->child, rep->_capacity, rep->_length, rep->_length, rep, format,
|
|
ok_writeable, ok_isNull, ok_references, ok_format);
|
|
}
|
|
else if (ok_references == false)
|
|
{
|
|
U_WARNING("UString::vsnprintf_check() this = %p parent = %p references = %u child = %d _capacity = %u str(%u) = %V format = %S - "
|
|
"ok_writeable = %b ok_isNull = %b ok_references = %b ok_format = %b",
|
|
this, rep->parent, rep->references, rep->child, rep->_capacity, rep->_length, rep->_length, rep, format,
|
|
ok_writeable, ok_isNull, ok_references, ok_format);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
|
|
const char* UString::dump(bool reset) const
|
|
{
|
|
U_CHECK_MEMORY_OBJECT(rep)
|
|
|
|
*UObjectIO::os << "rep (UStringRep " << (void*)rep << ")";
|
|
|
|
if (rep == rep->string_rep_null) *UObjectIO::os << " == UStringRepNull";
|
|
|
|
if (reset)
|
|
{
|
|
UObjectIO::output();
|
|
|
|
return UObjectIO::buffer_output;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|