summaryrefslogtreecommitdiffstats
path: root/sal/rtl
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sal/rtl
parentInitial commit. (diff)
downloadlibreoffice-upstream/4%7.4.7.tar.xz
libreoffice-upstream/4%7.4.7.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sal/rtl')
-rw-r--r--sal/rtl/alloc_arena.cxx1112
-rw-r--r--sal/rtl/alloc_arena.hxx118
-rw-r--r--sal/rtl/alloc_cache.cxx225
-rw-r--r--sal/rtl/alloc_cache.hxx40
-rw-r--r--sal/rtl/alloc_fini.cxx97
-rw-r--r--sal/rtl/alloc_global.cxx79
-rw-r--r--sal/rtl/alloc_impl.hxx219
-rw-r--r--sal/rtl/bootstrap.cxx992
-rw-r--r--sal/rtl/byteseq.cxx246
-rw-r--r--sal/rtl/cipher.cxx1433
-rw-r--r--sal/rtl/cmdargs.cxx103
-rw-r--r--sal/rtl/crc.cxx45
-rw-r--r--sal/rtl/digest.cxx1897
-rw-r--r--sal/rtl/hash.cxx241
-rw-r--r--sal/rtl/hash.hxx33
-rw-r--r--sal/rtl/locale.cxx136
-rw-r--r--sal/rtl/math.cxx828
-rw-r--r--sal/rtl/random.cxx310
-rw-r--r--sal/rtl/rtl_process.cxx56
-rw-r--r--sal/rtl/strbuf.cxx83
-rw-r--r--sal/rtl/strimp.cxx90
-rw-r--r--sal/rtl/strimp.hxx92
-rw-r--r--sal/rtl/string.cxx667
-rw-r--r--sal/rtl/strtmpl.hxx1904
-rw-r--r--sal/rtl/unload.cxx34
-rw-r--r--sal/rtl/uri.cxx759
-rw-r--r--sal/rtl/ustrbuf.cxx115
-rw-r--r--sal/rtl/ustring.cxx1324
-rw-r--r--sal/rtl/uuid.cxx173
29 files changed, 13451 insertions, 0 deletions
diff --git a/sal/rtl/alloc_arena.cxx b/sal/rtl/alloc_arena.cxx
new file mode 100644
index 000000000..f126efdab
--- /dev/null
+++ b/sal/rtl/alloc_arena.cxx
@@ -0,0 +1,1112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "alloc_arena.hxx"
+
+#include "alloc_impl.hxx"
+#include <rtllifecycle.h>
+#include <sal/macros.h>
+
+#include <cassert>
+#include <string.h>
+#include <stdio.h>
+
+namespace {
+
+/**
+ @internal
+*/
+struct rtl_arena_list_st
+{
+ rtl_memory_lock_type m_lock;
+ rtl_arena_type m_arena_head;
+};
+
+}
+
+static rtl_arena_list_st g_arena_list;
+
+/**
+ provided for arena_type allocations, and hash_table resizing.
+
+ @internal
+*/
+static rtl_arena_type * gp_arena_arena = nullptr;
+
+/**
+ Low level virtual memory (pseudo) arena
+ (platform dependent implementation)
+
+ @internal
+ */
+static rtl_arena_type * gp_machdep_arena = nullptr;
+
+rtl_arena_type * gp_default_arena = nullptr;
+
+namespace
+{
+
+void * rtl_machdep_alloc(
+ rtl_arena_type * pArena,
+ sal_Size * pSize
+);
+
+void rtl_machdep_free(
+ rtl_arena_type * pArena,
+ void * pAddr,
+ sal_Size nSize
+);
+
+sal_Size rtl_machdep_pagesize();
+
+void rtl_arena_segment_constructor(void * obj)
+{
+ rtl_arena_segment_type * segment = static_cast<rtl_arena_segment_type*>(obj);
+
+ QUEUE_START_NAMED(segment, s);
+ QUEUE_START_NAMED(segment, f);
+}
+
+void rtl_arena_segment_destructor(void * obj)
+{
+ rtl_arena_segment_type * segment = static_cast< rtl_arena_segment_type * >(
+ obj);
+ assert(QUEUE_STARTED_NAMED(segment, s));
+ assert(QUEUE_STARTED_NAMED(segment, f));
+ (void) segment; // avoid warnings
+}
+
+/**
+ @precond arena->m_lock acquired.
+ */
+bool rtl_arena_segment_populate(rtl_arena_type * arena)
+{
+ rtl_arena_segment_type *span;
+ sal_Size size = rtl_machdep_pagesize();
+
+ span = static_cast< rtl_arena_segment_type * >(
+ rtl_machdep_alloc(gp_machdep_arena, &size));
+ if (span)
+ {
+ rtl_arena_segment_type *first, *last, *head;
+ sal_Size count = size / sizeof(rtl_arena_segment_type);
+
+ /* insert onto reserve span list */
+ QUEUE_INSERT_TAIL_NAMED(&(arena->m_segment_reserve_span_head), span, s);
+ QUEUE_START_NAMED(span, f);
+ span->m_addr = reinterpret_cast<sal_uIntPtr>(span);
+ span->m_size = size;
+ span->m_type = RTL_ARENA_SEGMENT_TYPE_SPAN;
+
+ /* insert onto reserve list */
+ head = &(arena->m_segment_reserve_head);
+ for (first = span + 1, last = span + count; first < last; ++first)
+ {
+ QUEUE_INSERT_TAIL_NAMED(head, first, s);
+ QUEUE_START_NAMED(first, f);
+ first->m_addr = 0;
+ first->m_size = 0;
+ first->m_type = 0;
+ }
+ }
+ return (span != nullptr);
+}
+
+/**
+ @precond arena->m_lock acquired.
+ @precond (*ppSegment == 0)
+*/
+void rtl_arena_segment_get(
+ rtl_arena_type * arena,
+ rtl_arena_segment_type ** ppSegment
+)
+{
+ rtl_arena_segment_type * head;
+
+ assert(!*ppSegment);
+
+ head = &(arena->m_segment_reserve_head);
+ if (head->m_snext != head || rtl_arena_segment_populate (arena))
+ {
+ (*ppSegment) = head->m_snext;
+ QUEUE_REMOVE_NAMED(*ppSegment, s);
+ }
+}
+
+/**
+ @precond arena->m_lock acquired.
+ @postcond (*ppSegment == 0)
+ */
+void rtl_arena_segment_put(
+ rtl_arena_type * arena,
+ rtl_arena_segment_type ** ppSegment
+)
+{
+ rtl_arena_segment_type * head;
+
+ assert(QUEUE_STARTED_NAMED(*ppSegment, s));
+ assert(QUEUE_STARTED_NAMED(*ppSegment, f));
+
+ (*ppSegment)->m_addr = 0;
+ (*ppSegment)->m_size = 0;
+
+ assert((*ppSegment)->m_type != RTL_ARENA_SEGMENT_TYPE_HEAD);
+ (*ppSegment)->m_type = 0;
+
+ /* keep as reserve */
+ head = &(arena->m_segment_reserve_head);
+ QUEUE_INSERT_HEAD_NAMED(head, (*ppSegment), s);
+
+ /* clear */
+ (*ppSegment) = nullptr;
+}
+
+/**
+ @precond arena->m_lock acquired.
+*/
+void rtl_arena_freelist_insert (
+ rtl_arena_type * arena,
+ rtl_arena_segment_type * segment
+)
+{
+ rtl_arena_segment_type * head;
+ const auto bit = highbit(segment->m_size);
+ assert(bit > 0);
+ head = &(arena->m_freelist_head[bit - 1]);
+ QUEUE_INSERT_TAIL_NAMED(head, segment, f);
+
+ arena->m_freelist_bitmap |= head->m_size;
+}
+
+/**
+ @precond arena->m_lock acquired.
+*/
+void rtl_arena_freelist_remove(
+ rtl_arena_type * arena,
+ rtl_arena_segment_type * segment
+)
+{
+ if (segment->m_fnext->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD &&
+ segment->m_fprev->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD)
+ {
+ rtl_arena_segment_type * head;
+
+ head = segment->m_fprev;
+ assert(arena->m_freelist_bitmap & head->m_size);
+ arena->m_freelist_bitmap ^= head->m_size;
+ }
+ QUEUE_REMOVE_NAMED(segment, f);
+}
+
+#define RTL_ARENA_HASH_INDEX_IMPL(a, s, q, m) \
+ ((((a) + ((a) >> (s)) + ((a) >> ((s) << 1))) >> (q)) & (m))
+
+#define RTL_ARENA_HASH_INDEX(arena, addr) \
+ RTL_ARENA_HASH_INDEX_IMPL((addr), (arena)->m_hash_shift, (arena)->m_quantum_shift, ((arena)->m_hash_size - 1))
+
+/**
+ @precond arena->m_lock released.
+*/
+void rtl_arena_hash_rescale(
+ rtl_arena_type * arena,
+ sal_Size new_size
+)
+{
+ assert(new_size != 0);
+
+ rtl_arena_segment_type ** new_table;
+ sal_Size new_bytes;
+
+ new_bytes = new_size * sizeof(rtl_arena_segment_type*);
+ new_table = static_cast<rtl_arena_segment_type **>(rtl_arena_alloc (gp_arena_arena, &new_bytes));
+
+ if (new_table)
+ {
+ rtl_arena_segment_type ** old_table;
+ sal_Size old_size, i;
+
+ memset (new_table, 0, new_bytes);
+
+ RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
+
+ old_table = arena->m_hash_table;
+ old_size = arena->m_hash_size;
+
+ arena->m_hash_table = new_table;
+ arena->m_hash_size = new_size;
+ arena->m_hash_shift = highbit(arena->m_hash_size) - 1;
+
+ for (i = 0; i < old_size; i++)
+ {
+ rtl_arena_segment_type * curr = old_table[i];
+ while (curr)
+ {
+ rtl_arena_segment_type * next = curr->m_fnext;
+ rtl_arena_segment_type ** head;
+
+ // coverity[negative_shift] - bogus
+ head = &(arena->m_hash_table[RTL_ARENA_HASH_INDEX(arena, curr->m_addr)]);
+ curr->m_fnext = (*head);
+ (*head) = curr;
+
+ curr = next;
+ }
+ old_table[i] = nullptr;
+ }
+
+ RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
+
+ if (old_table != arena->m_hash_table_0)
+ {
+ sal_Size old_bytes = old_size * sizeof(rtl_arena_segment_type*);
+ rtl_arena_free (gp_arena_arena, old_table, old_bytes);
+ }
+ }
+}
+
+/**
+ Insert arena hash, and update stats.
+*/
+void rtl_arena_hash_insert(
+ rtl_arena_type * arena,
+ rtl_arena_segment_type * segment
+)
+{
+ rtl_arena_segment_type ** ppSegment;
+
+ ppSegment = &(arena->m_hash_table[RTL_ARENA_HASH_INDEX(arena, segment->m_addr)]);
+
+ segment->m_fnext = (*ppSegment);
+ (*ppSegment) = segment;
+
+ arena->m_stats.m_alloc += 1;
+ arena->m_stats.m_mem_alloc += segment->m_size;
+}
+
+/**
+ Remove arena hash, and update stats.
+*/
+rtl_arena_segment_type * rtl_arena_hash_remove(
+ rtl_arena_type * arena,
+ sal_uIntPtr addr,
+ sal_Size size
+)
+{
+ rtl_arena_segment_type *segment, **segpp;
+ sal_Size lookups = 0;
+
+ segpp = &(arena->m_hash_table[RTL_ARENA_HASH_INDEX(arena, addr)]);
+ while ((segment = *segpp))
+ {
+ if (segment->m_addr == addr)
+ {
+ *segpp = segment->m_fnext;
+ segment->m_fnext = segment->m_fprev = segment;
+ break;
+ }
+
+ /* update lookup miss stats */
+ lookups += 1;
+ segpp = &(segment->m_fnext);
+ }
+
+ assert(segment); // bad free
+ if (segment)
+ {
+ assert(segment->m_size == size);
+ (void) size; // avoid warnings
+
+ arena->m_stats.m_free += 1;
+ arena->m_stats.m_mem_alloc -= segment->m_size;
+
+ if (lookups > 1)
+ {
+ sal_Size nseg = static_cast<sal_Size>(arena->m_stats.m_alloc - arena->m_stats.m_free);
+ if (nseg > 4 * arena->m_hash_size)
+ {
+ if (!(arena->m_flags & RTL_ARENA_FLAG_RESCALE))
+ {
+ sal_Size ave = nseg >> arena->m_hash_shift;
+ assert(ave != 0);
+ sal_Size new_size = arena->m_hash_size << (highbit(ave) - 1);
+
+ arena->m_flags |= RTL_ARENA_FLAG_RESCALE;
+ RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
+ rtl_arena_hash_rescale (arena, new_size);
+ RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
+ arena->m_flags &= ~RTL_ARENA_FLAG_RESCALE;
+ }
+ }
+ }
+ }
+
+ return segment;
+}
+
+/**
+ allocate (and remove) segment from freelist
+
+ @precond arena->m_lock acquired
+ @precond (*ppSegment == 0)
+*/
+bool rtl_arena_segment_alloc(
+ rtl_arena_type * arena,
+ sal_Size size,
+ rtl_arena_segment_type ** ppSegment
+)
+{
+ int index = 0;
+
+ assert(!*ppSegment);
+ if (!RTL_MEMORY_ISP2(size))
+ {
+ unsigned int msb = highbit(size);
+ if (RTL_ARENA_FREELIST_SIZE == msb)
+ {
+ /* highest possible freelist: fall back to first fit */
+ rtl_arena_segment_type *head, *segment;
+
+ head = &(arena->m_freelist_head[msb - 1]);
+ for (segment = head->m_fnext; segment != head; segment = segment->m_fnext)
+ {
+ if (segment->m_size >= size)
+ {
+ /* allocate first fit segment */
+ (*ppSegment) = segment;
+ break;
+ }
+ }
+ goto dequeue_and_leave;
+ }
+
+ /* roundup to next power of 2 */
+ size = ((sal_Size(1)) << msb);
+ }
+
+ index = lowbit(RTL_MEMORY_P2ALIGN(arena->m_freelist_bitmap, size));
+ if (index > 0)
+ {
+ /* instant fit: allocate first free segment */
+ rtl_arena_segment_type *head;
+
+ head = &(arena->m_freelist_head[index - 1]);
+ (*ppSegment) = head->m_fnext;
+ assert((*ppSegment) != head);
+ }
+
+dequeue_and_leave:
+ if (*ppSegment)
+ {
+ /* remove from freelist */
+ rtl_arena_freelist_remove (arena, (*ppSegment));
+ }
+ return (*ppSegment != nullptr);
+}
+
+/**
+ import new (span) segment from source arena
+
+ @precond arena->m_lock acquired
+ @precond (*ppSegment == 0)
+*/
+bool rtl_arena_segment_create(
+ rtl_arena_type * arena,
+ sal_Size size,
+ rtl_arena_segment_type ** ppSegment
+)
+{
+ assert(!*ppSegment);
+ if (arena->m_source_alloc)
+ {
+ rtl_arena_segment_get (arena, ppSegment);
+ if (*ppSegment)
+ {
+ rtl_arena_segment_type * span = nullptr;
+ rtl_arena_segment_get (arena, &span);
+ if (span)
+ {
+ /* import new span from source arena */
+ RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
+
+ span->m_size = size;
+ span->m_addr = reinterpret_cast<sal_uIntPtr>(
+ (arena->m_source_alloc)(
+ arena->m_source_arena, &(span->m_size)));
+
+ RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
+ if (span->m_addr != 0)
+ {
+ /* insert onto segment list, update stats */
+ span->m_type = RTL_ARENA_SEGMENT_TYPE_SPAN;
+ QUEUE_INSERT_HEAD_NAMED(&(arena->m_segment_head), span, s);
+ arena->m_stats.m_mem_total += span->m_size;
+
+ (*ppSegment)->m_addr = span->m_addr;
+ (*ppSegment)->m_size = span->m_size;
+ (*ppSegment)->m_type = RTL_ARENA_SEGMENT_TYPE_FREE;
+ QUEUE_INSERT_HEAD_NAMED(span, (*ppSegment), s);
+
+ /* report success */
+ return true;
+ }
+ rtl_arena_segment_put (arena, &span);
+ }
+ rtl_arena_segment_put (arena, ppSegment);
+ }
+ }
+ return false; // failure
+}
+
+/**
+ mark as free and join with adjacent free segment(s)
+
+ @precond arena->m_lock acquired
+ @precond segment marked 'used'
+*/
+void rtl_arena_segment_coalesce(
+ rtl_arena_type * arena,
+ rtl_arena_segment_type * segment
+)
+{
+ rtl_arena_segment_type *next, *prev;
+
+ /* mark segment free */
+ assert(segment->m_type == RTL_ARENA_SEGMENT_TYPE_USED);
+ segment->m_type = RTL_ARENA_SEGMENT_TYPE_FREE;
+
+ /* try to merge w/ next segment */
+ next = segment->m_snext;
+ if (next->m_type == RTL_ARENA_SEGMENT_TYPE_FREE)
+ {
+ assert(segment->m_addr + segment->m_size == next->m_addr);
+ segment->m_size += next->m_size;
+
+ /* remove from freelist */
+ rtl_arena_freelist_remove (arena, next);
+
+ /* remove from segment list */
+ QUEUE_REMOVE_NAMED(next, s);
+
+ /* release segment descriptor */
+ rtl_arena_segment_put (arena, &next);
+ }
+
+ /* try to merge w/ prev segment */
+ prev = segment->m_sprev;
+ if (prev->m_type == RTL_ARENA_SEGMENT_TYPE_FREE)
+ {
+ assert(prev->m_addr + prev->m_size == segment->m_addr);
+ segment->m_addr = prev->m_addr;
+ segment->m_size += prev->m_size;
+
+ /* remove from freelist */
+ rtl_arena_freelist_remove (arena, prev);
+
+ /* remove from segment list */
+ QUEUE_REMOVE_NAMED(prev, s);
+
+ /* release segment descriptor */
+ rtl_arena_segment_put (arena, &prev);
+ }
+}
+
+void rtl_arena_constructor(void * obj)
+{
+ rtl_arena_type * arena = static_cast<rtl_arena_type*>(obj);
+ rtl_arena_segment_type * head;
+ size_t i;
+
+ memset (arena, 0, sizeof(rtl_arena_type));
+
+ QUEUE_START_NAMED(arena, arena_);
+
+ RTL_MEMORY_LOCK_INIT(&(arena->m_lock));
+
+ head = &(arena->m_segment_reserve_span_head);
+ rtl_arena_segment_constructor (head);
+ head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD;
+
+ head = &(arena->m_segment_reserve_head);
+ rtl_arena_segment_constructor (head);
+ head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD;
+
+ head = &(arena->m_segment_head);
+ rtl_arena_segment_constructor (head);
+ head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD;
+
+ for (i = 0; i < RTL_ARENA_FREELIST_SIZE; i++)
+ {
+ head = &(arena->m_freelist_head[i]);
+ rtl_arena_segment_constructor (head);
+
+ head->m_size = ((sal_Size(1)) << i);
+ head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD;
+ }
+
+ arena->m_hash_table = arena->m_hash_table_0;
+ arena->m_hash_size = RTL_ARENA_HASH_SIZE;
+ arena->m_hash_shift = highbit(arena->m_hash_size) - 1;
+}
+
+void rtl_arena_destructor(void * obj)
+{
+ rtl_arena_type * arena = static_cast<rtl_arena_type*>(obj);
+ rtl_arena_segment_type * head;
+ size_t i;
+
+ assert(QUEUE_STARTED_NAMED(arena, arena_));
+
+ RTL_MEMORY_LOCK_DESTROY(&(arena->m_lock));
+
+ head = &(arena->m_segment_reserve_span_head);
+ assert(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD);
+ rtl_arena_segment_destructor (head);
+
+ head = &(arena->m_segment_reserve_head);
+ assert(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD);
+ rtl_arena_segment_destructor (head);
+
+ head = &(arena->m_segment_head);
+ assert(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD);
+ rtl_arena_segment_destructor (head);
+
+ for (i = 0; i < RTL_ARENA_FREELIST_SIZE; i++)
+ {
+ head = &(arena->m_freelist_head[i]);
+
+ assert(head->m_size == ((sal_Size(1)) << i));
+ assert(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD);
+
+ rtl_arena_segment_destructor (head);
+ }
+
+ assert(arena->m_hash_table == arena->m_hash_table_0);
+ assert(arena->m_hash_size == RTL_ARENA_HASH_SIZE);
+ assert(arena->m_hash_shift == highbit(arena->m_hash_size) - 1);
+}
+
+rtl_arena_type * rtl_arena_activate(
+ rtl_arena_type * arena,
+ const char * name,
+ sal_Size quantum,
+ rtl_arena_type * source_arena,
+ void * (SAL_CALL * source_alloc)(rtl_arena_type *, sal_Size *),
+ void (SAL_CALL * source_free) (rtl_arena_type *, void *, sal_Size)
+)
+{
+ assert(arena);
+ if (arena)
+ {
+ (void) snprintf (arena->m_name, sizeof(arena->m_name), "%s", name);
+
+ if (!RTL_MEMORY_ISP2(quantum))
+ {
+ /* roundup to next power of 2 */
+ quantum = ((sal_Size(1)) << highbit(quantum));
+ }
+
+ arena->m_quantum = quantum;
+ arena->m_quantum_shift = highbit(arena->m_quantum) - 1;
+
+ arena->m_source_arena = source_arena;
+ arena->m_source_alloc = source_alloc;
+ arena->m_source_free = source_free;
+
+ /* insert into arena list */
+ RTL_MEMORY_LOCK_ACQUIRE(&(g_arena_list.m_lock));
+ QUEUE_INSERT_TAIL_NAMED(&(g_arena_list.m_arena_head), arena, arena_);
+ RTL_MEMORY_LOCK_RELEASE(&(g_arena_list.m_lock));
+ }
+ return arena;
+}
+
+void rtl_arena_deactivate(rtl_arena_type * arena)
+{
+ rtl_arena_segment_type * head, * segment;
+
+ /* remove from arena list */
+ RTL_MEMORY_LOCK_ACQUIRE(&(g_arena_list.m_lock));
+ QUEUE_REMOVE_NAMED(arena, arena_);
+ RTL_MEMORY_LOCK_RELEASE(&(g_arena_list.m_lock));
+
+ /* check for leaked segments */
+ if (arena->m_stats.m_alloc > arena->m_stats.m_free)
+ {
+ sal_Size i, n;
+
+ /* cleanup still used segment(s) */
+ for (i = 0, n = arena->m_hash_size; i < n; i++)
+ {
+ while ((segment = arena->m_hash_table[i]))
+ {
+ /* pop from hash table */
+ arena->m_hash_table[i] = segment->m_fnext;
+ segment->m_fnext = segment->m_fprev = segment;
+
+ /* coalesce w/ adjacent free segment(s) */
+ rtl_arena_segment_coalesce (arena, segment);
+
+ /* insert onto freelist */
+ rtl_arena_freelist_insert (arena, segment);
+ }
+ }
+ }
+
+ /* cleanup hash table */
+ if (arena->m_hash_table != arena->m_hash_table_0)
+ {
+ rtl_arena_free(
+ gp_arena_arena,
+ arena->m_hash_table,
+ arena->m_hash_size * sizeof(rtl_arena_segment_type*));
+
+ arena->m_hash_table = arena->m_hash_table_0;
+ arena->m_hash_size = RTL_ARENA_HASH_SIZE;
+ arena->m_hash_shift = highbit(arena->m_hash_size) - 1;
+ }
+
+ /* cleanup segment list */
+ head = &(arena->m_segment_head);
+ for (segment = head->m_snext; segment != head; segment = head->m_snext)
+ {
+ if (segment->m_type == RTL_ARENA_SEGMENT_TYPE_FREE)
+ {
+ /* remove from freelist */
+ rtl_arena_freelist_remove (arena, segment);
+ }
+ else
+ {
+ /* can have only free and span segments here */
+ assert(segment->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN);
+ }
+
+ /* remove from segment list */
+ QUEUE_REMOVE_NAMED(segment, s);
+
+ /* release segment descriptor */
+ rtl_arena_segment_put (arena, &segment);
+ }
+
+ /* cleanup segment reserve list */
+ head = &(arena->m_segment_reserve_head);
+ for (segment = head->m_snext; segment != head; segment = head->m_snext)
+ {
+ /* remove from segment list */
+ QUEUE_REMOVE_NAMED(segment, s);
+ }
+
+ /* cleanup segment reserve span(s) */
+ head = &(arena->m_segment_reserve_span_head);
+ for (segment = head->m_snext; segment != head; segment = head->m_snext)
+ {
+ /* can have only span segments here */
+ assert(segment->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN);
+
+ /* remove from segment list */
+ QUEUE_REMOVE_NAMED(segment, s);
+
+ /* return span to g_machdep_arena */
+ rtl_machdep_free (gp_machdep_arena, reinterpret_cast<void*>(segment->m_addr), segment->m_size);
+ }
+}
+
+} // namespace
+
+rtl_arena_type * SAL_CALL rtl_arena_create(
+ const char * name,
+ sal_Size quantum,
+ sal_Size,
+ rtl_arena_type * source_arena,
+ void * (SAL_CALL * source_alloc)(rtl_arena_type *, sal_Size *),
+ void (SAL_CALL * source_free) (rtl_arena_type *, void *, sal_Size),
+ SAL_UNUSED_PARAMETER int
+) SAL_THROW_EXTERN_C()
+{
+ rtl_arena_type * result = nullptr;
+ sal_Size size = sizeof(rtl_arena_type);
+
+try_alloc:
+ result = static_cast<rtl_arena_type*>(rtl_arena_alloc (gp_arena_arena, &size));
+ if (result)
+ {
+ rtl_arena_type * arena = result;
+ rtl_arena_constructor (arena);
+
+ if (!source_arena)
+ {
+ assert(gp_default_arena);
+ source_arena = gp_default_arena;
+ }
+
+ result = rtl_arena_activate (
+ arena,
+ name,
+ quantum,
+ source_arena,
+ source_alloc,
+ source_free
+ );
+
+ if (!result)
+ {
+ rtl_arena_deactivate (arena);
+ rtl_arena_destructor (arena);
+ rtl_arena_free (gp_arena_arena, arena, size);
+ }
+ }
+ else if (!gp_arena_arena)
+ {
+ ensureArenaSingleton();
+ if (gp_arena_arena)
+ {
+ /* try again */
+ goto try_alloc;
+ }
+ }
+ return result;
+}
+
+void SAL_CALL rtl_arena_destroy(rtl_arena_type * arena) SAL_THROW_EXTERN_C()
+{
+ if (arena)
+ {
+ rtl_arena_deactivate (arena);
+ rtl_arena_destructor (arena);
+ rtl_arena_free (gp_arena_arena, arena, sizeof(rtl_arena_type));
+ }
+}
+
+void * SAL_CALL rtl_arena_alloc(
+ rtl_arena_type * arena,
+ sal_Size * pSize
+) SAL_THROW_EXTERN_C()
+{
+ void * addr = nullptr;
+
+ if (arena && pSize)
+ {
+ sal_Size size;
+
+ size = RTL_MEMORY_ALIGN(*pSize, arena->m_quantum);
+ if (size > 0)
+ {
+ /* allocate from segment list */
+ rtl_arena_segment_type *segment = nullptr;
+
+ RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
+ if (rtl_arena_segment_alloc (arena, size, &segment) ||
+ rtl_arena_segment_create(arena, size, &segment) )
+ {
+ /* shrink to fit */
+ sal_Size oversize;
+
+ /* mark segment used */
+ assert(segment->m_type == RTL_ARENA_SEGMENT_TYPE_FREE);
+ segment->m_type = RTL_ARENA_SEGMENT_TYPE_USED;
+
+ /* resize */
+ assert(segment->m_size >= size);
+ oversize = segment->m_size - size;
+ if (oversize >= arena->m_quantum)
+ {
+ rtl_arena_segment_type * remainder = nullptr;
+ rtl_arena_segment_get (arena, &remainder);
+ if (remainder)
+ {
+ segment->m_size = size;
+
+ remainder->m_addr = segment->m_addr + segment->m_size;
+ remainder->m_size = oversize;
+ remainder->m_type = RTL_ARENA_SEGMENT_TYPE_FREE;
+ QUEUE_INSERT_HEAD_NAMED(segment, remainder, s);
+
+ rtl_arena_freelist_insert (arena, remainder);
+ }
+ }
+
+ rtl_arena_hash_insert (arena, segment);
+
+ (*pSize) = segment->m_size;
+ addr = reinterpret_cast<void*>(segment->m_addr);
+ }
+ RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
+ }
+ }
+ return addr;
+}
+
+void SAL_CALL rtl_arena_free (
+ rtl_arena_type * arena,
+ void * addr,
+ sal_Size size
+) SAL_THROW_EXTERN_C()
+{
+ if (arena)
+ {
+ size = RTL_MEMORY_ALIGN(size, arena->m_quantum);
+ if (size > 0)
+ {
+ /* free to segment list */
+ rtl_arena_segment_type * segment;
+
+ RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
+
+ segment = rtl_arena_hash_remove (arena, reinterpret_cast<sal_uIntPtr>(addr), size);
+ if (segment)
+ {
+ rtl_arena_segment_type *next, *prev;
+
+ /* coalesce w/ adjacent free segment(s) */
+ rtl_arena_segment_coalesce (arena, segment);
+
+ /* determine (new) next and prev segment */
+ next = segment->m_snext;
+ prev = segment->m_sprev;
+
+ /* entire span free when prev is a span, and next is either a span or a list head */
+ if (prev->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN &&
+ ((next->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN) ||
+ (next->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD)))
+ {
+ assert(
+ prev->m_addr == segment->m_addr
+ && prev->m_size == segment->m_size);
+
+ if (arena->m_source_free)
+ {
+ addr = reinterpret_cast<void*>(prev->m_addr);
+ size = prev->m_size;
+
+ /* remove from segment list */
+ QUEUE_REMOVE_NAMED(segment, s);
+
+ /* release segment descriptor */
+ rtl_arena_segment_put (arena, &segment);
+
+ /* remove from segment list */
+ QUEUE_REMOVE_NAMED(prev, s);
+
+ /* release (span) segment descriptor */
+ rtl_arena_segment_put (arena, &prev);
+
+ /* update stats, return span to source arena */
+ arena->m_stats.m_mem_total -= size;
+ RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
+
+ (arena->m_source_free)(arena->m_source_arena, addr, size);
+ return;
+ }
+ }
+
+ /* insert onto freelist */
+ rtl_arena_freelist_insert (arena, segment);
+ }
+
+ RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
+ }
+ }
+}
+
+void rtl_arena_foreach (rtl_arena_type *arena, ArenaForeachFn foreachFn)
+{
+ /* used segments */
+ for (int i = 0, n = arena->m_hash_size; i < n; i++)
+ {
+ for (rtl_arena_segment_type *segment = arena->m_hash_table[i];
+ segment != nullptr; segment = segment->m_fnext)
+ {
+ foreachFn(reinterpret_cast<void *>(segment->m_addr),
+ segment->m_size);
+ }
+ }
+}
+
+#if defined(SAL_UNX)
+#include <sys/mman.h>
+#elif defined(_WIN32)
+#define MAP_FAILED nullptr
+#endif /* SAL_UNX || _WIN32 */
+
+namespace
+{
+
+void * rtl_machdep_alloc(
+ rtl_arena_type * pArena,
+ sal_Size * pSize
+)
+{
+ void * addr;
+ sal_Size size = *pSize;
+
+ assert(pArena == gp_machdep_arena);
+
+#if defined(__sun) && defined(SPARC)
+ /* see @ mmap(2) man pages */
+ size += (pArena->m_quantum + pArena->m_quantum); /* "red-zone" pages */
+ if (size > (4 << 20))
+ size = RTL_MEMORY_P2ROUNDUP(size, (4 << 20));
+ else if (size > (512 << 10))
+ size = RTL_MEMORY_P2ROUNDUP(size, (512 << 10));
+ else
+ size = RTL_MEMORY_P2ROUNDUP(size, (64 << 10));
+ size -= (pArena->m_quantum + pArena->m_quantum); /* "red-zone" pages */
+#else
+ /* default allocation granularity */
+ if (pArena->m_quantum < (64 << 10))
+ {
+ size = RTL_MEMORY_P2ROUNDUP(size, (64 << 10));
+ }
+ else
+ {
+ size = RTL_MEMORY_P2ROUNDUP(size, pArena->m_quantum);
+ }
+#endif
+
+#if defined(SAL_UNX)
+ addr = mmap (nullptr, static_cast<size_t>(size), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+#elif defined(_WIN32)
+ addr = VirtualAlloc (nullptr, static_cast<SIZE_T>(size), MEM_COMMIT, PAGE_READWRITE);
+#endif /* (SAL_UNX || _WIN32) */
+
+ if (addr != MAP_FAILED)
+ {
+ pArena->m_stats.m_alloc += 1;
+ pArena->m_stats.m_mem_total += size;
+ pArena->m_stats.m_mem_alloc += size;
+
+ (*pSize) = size;
+ return addr;
+ }
+ return nullptr;
+}
+
+void rtl_machdep_free(
+ rtl_arena_type * pArena,
+ void * pAddr,
+ sal_Size nSize
+)
+{
+ assert(pArena == gp_machdep_arena);
+
+ pArena->m_stats.m_free += 1;
+ pArena->m_stats.m_mem_total -= nSize;
+ pArena->m_stats.m_mem_alloc -= nSize;
+
+#if defined(SAL_UNX)
+ (void) munmap(pAddr, nSize);
+#elif defined(_WIN32)
+ (void) VirtualFree (pAddr, SIZE_T(0), MEM_RELEASE);
+#endif /* (SAL_UNX || _WIN32) */
+}
+
+sal_Size rtl_machdep_pagesize()
+{
+#if defined(SAL_UNX)
+#if defined(FREEBSD) || defined(NETBSD) || defined(DRAGONFLY)
+ return (sal_Size)getpagesize();
+#else /* POSIX */
+ return static_cast<sal_Size>(sysconf(_SC_PAGESIZE));
+#endif /* xBSD || POSIX */
+#elif defined(_WIN32)
+ SYSTEM_INFO info;
+ GetSystemInfo (&info);
+ return static_cast<sal_Size>(info.dwPageSize);
+#endif /* (SAL_UNX || _WIN32) */
+}
+
+} //namespace
+
+void rtl_arena_init()
+{
+ {
+ /* list of arenas */
+ RTL_MEMORY_LOCK_INIT(&(g_arena_list.m_lock));
+ rtl_arena_constructor (&(g_arena_list.m_arena_head));
+ }
+ {
+ /* machdep (pseudo) arena */
+ static rtl_arena_type g_machdep_arena;
+
+ assert(!gp_machdep_arena);
+ rtl_arena_constructor (&g_machdep_arena);
+
+ gp_machdep_arena = rtl_arena_activate (
+ &g_machdep_arena,
+ "rtl_machdep_arena",
+ rtl_machdep_pagesize(),
+ nullptr, nullptr, nullptr /* no source */
+ );
+ assert(gp_machdep_arena);
+ }
+ {
+ /* default arena */
+ static rtl_arena_type g_default_arena;
+
+ assert(!gp_default_arena);
+ rtl_arena_constructor (&g_default_arena);
+
+ gp_default_arena = rtl_arena_activate (
+ &g_default_arena,
+ "rtl_default_arena",
+ rtl_machdep_pagesize(),
+ gp_machdep_arena, /* source */
+ rtl_machdep_alloc,
+ rtl_machdep_free
+ );
+ assert(gp_default_arena);
+ }
+ {
+ /* arena internal arena */
+ static rtl_arena_type g_arena_arena;
+
+ assert(!gp_arena_arena);
+ rtl_arena_constructor (&g_arena_arena);
+
+ gp_arena_arena = rtl_arena_activate(
+ &g_arena_arena,
+ "rtl_arena_internal_arena",
+ 64, /* quantum */
+ gp_default_arena, /* source */
+ rtl_arena_alloc,
+ rtl_arena_free
+ );
+ assert(gp_arena_arena);
+ }
+}
+
+void rtl_arena_fini()
+{
+ if (gp_arena_arena)
+ {
+ rtl_arena_type * arena, * head;
+
+ RTL_MEMORY_LOCK_ACQUIRE(&(g_arena_list.m_lock));
+ head = &(g_arena_list.m_arena_head);
+
+ for (arena = head->m_arena_next; arena != head; arena = arena->m_arena_next)
+ {
+ // noop
+ }
+ RTL_MEMORY_LOCK_RELEASE(&(g_arena_list.m_lock));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/alloc_arena.hxx b/sal/rtl/alloc_arena.hxx
new file mode 100644
index 000000000..7226ef3f1
--- /dev/null
+++ b/sal/rtl/alloc_arena.hxx
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_RTL_ALLOC_ARENA_HXX
+#define INCLUDED_SAL_RTL_ALLOC_ARENA_HXX
+
+#include <sal/types.h>
+#include <rtl/alloc.h>
+#include "alloc_impl.hxx"
+
+/** rtl_arena_stat_type
+ * @internal
+ */
+struct rtl_arena_stat_type
+{
+ sal_uInt64 m_alloc;
+ sal_uInt64 m_free;
+
+ sal_Size m_mem_total;
+ sal_Size m_mem_alloc;
+};
+
+/** rtl_arena_segment_type
+ * @internal
+ */
+constexpr sal_Size RTL_ARENA_SEGMENT_TYPE_HEAD = 0x01;
+constexpr sal_Size RTL_ARENA_SEGMENT_TYPE_SPAN = 0x02;
+constexpr sal_Size RTL_ARENA_SEGMENT_TYPE_FREE = 0x04;
+constexpr sal_Size RTL_ARENA_SEGMENT_TYPE_USED = 0x08;
+
+struct rtl_arena_segment_type
+{
+ /* segment list linkage */
+ rtl_arena_segment_type * m_snext;
+ rtl_arena_segment_type * m_sprev;
+
+ /* free/used list linkage */
+ rtl_arena_segment_type * m_fnext;
+ rtl_arena_segment_type * m_fprev;
+
+ /* segment description */
+ sal_uIntPtr m_addr;
+ sal_Size m_size;
+ sal_Size m_type;
+};
+
+/** rtl_arena_type
+ * @internal
+ */
+constexpr auto RTL_ARENA_FREELIST_SIZE = sizeof(void*) * 8;
+constexpr auto RTL_ARENA_HASH_SIZE = 64;
+
+constexpr auto RTL_ARENA_FLAG_RESCALE = 1; /* within hash rescale operation */
+
+struct rtl_arena_st
+{
+ /* linkage */
+ rtl_arena_type * m_arena_next;
+ rtl_arena_type * m_arena_prev;
+
+ /* properties */
+ char m_name[RTL_ARENA_NAME_LENGTH + 1];
+ long m_flags;
+
+ rtl_memory_lock_type m_lock;
+ rtl_arena_stat_type m_stats;
+
+ rtl_arena_type * m_source_arena;
+ void * (SAL_CALL * m_source_alloc)(rtl_arena_type *, sal_Size *);
+ void (SAL_CALL * m_source_free) (rtl_arena_type *, void *, sal_Size);
+
+ sal_Size m_quantum;
+ sal_Size m_quantum_shift; /* log2(m_quantum) */
+
+ rtl_arena_segment_type m_segment_reserve_span_head;
+ rtl_arena_segment_type m_segment_reserve_head;
+
+ rtl_arena_segment_type m_segment_head;
+
+ rtl_arena_segment_type m_freelist_head[RTL_ARENA_FREELIST_SIZE];
+ sal_Size m_freelist_bitmap;
+
+ rtl_arena_segment_type ** m_hash_table;
+ rtl_arena_segment_type * m_hash_table_0[RTL_ARENA_HASH_SIZE];
+ sal_Size m_hash_size; /* m_hash_mask + 1 */
+ sal_Size m_hash_shift; /* log2(m_hash_size) */
+};
+
+/** gp_default_arena
+ * default arena with pagesize quantum
+ *
+ * @internal
+ */
+extern rtl_arena_type * gp_default_arena;
+
+typedef void (*ArenaForeachFn)(void *addr, sal_Size size);
+
+void rtl_arena_foreach(rtl_arena_type *arena, ArenaForeachFn fn);
+
+#endif // INCLUDED_SAL_RTL_ALLOC_ARENA_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/alloc_cache.cxx b/sal/rtl/alloc_cache.cxx
new file mode 100644
index 000000000..1f165cca1
--- /dev/null
+++ b/sal/rtl/alloc_cache.cxx
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "alloc_cache.hxx"
+#include "alloc_impl.hxx"
+#include "alloc_arena.hxx"
+#include <rtllifecycle.h>
+
+#include <cassert>
+#include <cstdlib>
+#include <string.h>
+#include <stdio.h>
+
+/**
+ provided for cache_type allocations, and hash_table resizing.
+
+ @internal
+*/
+static rtl_arena_type * gp_cache_arena = nullptr;
+
+namespace
+{
+
+rtl_cache_type * rtl_cache_activate(
+ rtl_cache_type * cache,
+ const char * name,
+ size_t objsize,
+ size_t objalign,
+ int (SAL_CALL * constructor)(void * obj, void * userarg),
+ void (SAL_CALL * destructor) (void * obj, void * userarg),
+ void * userarg
+)
+{
+ assert(cache);
+
+ snprintf (cache->m_name, sizeof(cache->m_name), "%s", name);
+
+ if (objalign == 0)
+ {
+ /* determine default alignment */
+ if (objsize >= RTL_MEMORY_ALIGNMENT_8)
+ objalign = RTL_MEMORY_ALIGNMENT_8;
+ else
+ objalign = RTL_MEMORY_ALIGNMENT_4;
+ }
+ else
+ {
+ /* ensure minimum alignment */
+ if(objalign < RTL_MEMORY_ALIGNMENT_4)
+ {
+ objalign = RTL_MEMORY_ALIGNMENT_4;
+ }
+ }
+ assert(RTL_MEMORY_ISP2(objalign));
+
+ cache->m_type_size = RTL_MEMORY_P2ROUNDUP(objsize, objalign);
+
+ cache->m_constructor = constructor;
+ cache->m_destructor = destructor;
+ cache->m_userarg = userarg;
+
+ return cache;
+}
+
+} //namespace
+
+rtl_cache_type * SAL_CALL rtl_cache_create(
+ const char * name,
+ sal_Size objsize,
+ sal_Size objalign,
+ int (SAL_CALL * constructor)(void * obj, void * userarg),
+ void (SAL_CALL * destructor) (void * obj, void * userarg),
+ void (SAL_CALL * /*reclaim*/) (void * userarg),
+ void * userarg,
+ rtl_arena_type *,
+ int
+) SAL_THROW_EXTERN_C()
+{
+ rtl_cache_type * result = nullptr;
+ sal_Size size = sizeof(rtl_cache_type);
+
+try_alloc:
+ result = static_cast<rtl_cache_type*>(rtl_arena_alloc (gp_cache_arena, &size));
+ if (result)
+ {
+ rtl_cache_type * cache = result;
+ memset (cache, 0, sizeof(rtl_cache_type));
+
+ result = rtl_cache_activate (
+ cache,
+ name,
+ objsize,
+ objalign,
+ constructor,
+ destructor,
+ userarg
+ );
+
+ if (!result)
+ {
+ /* activation failed */
+ rtl_arena_free (gp_cache_arena, cache, size);
+ }
+ }
+ else if (!gp_cache_arena)
+ {
+ ensureCacheSingleton();
+ if (gp_cache_arena)
+ {
+ /* try again */
+ goto try_alloc;
+ }
+ }
+ return result;
+}
+
+void SAL_CALL rtl_cache_destroy(rtl_cache_type * cache) SAL_THROW_EXTERN_C()
+{
+ if (cache)
+ {
+ rtl_arena_free (gp_cache_arena, cache, sizeof(rtl_cache_type));
+ }
+}
+
+void * SAL_CALL rtl_cache_alloc(rtl_cache_type * cache) SAL_THROW_EXTERN_C()
+{
+ void * obj = nullptr;
+
+ if (!cache)
+ return nullptr;
+
+ obj = std::malloc(cache->m_type_size);
+ if (obj && cache->m_constructor)
+ {
+ if (!(cache->m_constructor)(obj, cache->m_userarg))
+ {
+ /* construction failure */
+ std::free(obj);
+ obj = nullptr;
+ }
+ }
+ return obj;
+}
+
+void SAL_CALL rtl_cache_free(
+ rtl_cache_type * cache,
+ void * obj
+) SAL_THROW_EXTERN_C()
+{
+ if (obj && cache)
+ {
+ if (cache->m_destructor)
+ {
+ /* destruct object */
+ (cache->m_destructor)(obj, cache->m_userarg);
+ }
+ std::free(obj);
+ }
+}
+
+#if defined(SAL_UNX)
+
+void SAL_CALL rtl_secureZeroMemory(void *Ptr, sal_Size Bytes) SAL_THROW_EXTERN_C()
+{
+ //currently glibc doesn't implement memset_s
+ volatile char *p = static_cast<volatile char*>(Ptr);
+ while (Bytes--)
+ *p++ = 0;
+}
+
+#elif defined(_WIN32)
+
+void SAL_CALL rtl_secureZeroMemory(void *Ptr, sal_Size Bytes) SAL_THROW_EXTERN_C()
+{
+ RtlSecureZeroMemory(Ptr, Bytes);
+}
+
+#endif /* SAL_UNX || _WIN32 */
+
+void rtl_cache_init()
+{
+ /* cache: internal arena */
+ assert(!gp_cache_arena);
+
+ gp_cache_arena = rtl_arena_create (
+ "rtl_cache_internal_arena",
+ 64, /* quantum */
+ 0, /* no quantum caching */
+ nullptr, /* default source */
+ rtl_arena_alloc,
+ rtl_arena_free,
+ 0 /* flags */
+ );
+ assert(gp_cache_arena);
+
+ /* check 'gp_default_arena' initialization */
+ assert(gp_default_arena);
+}
+
+void rtl_cache_fini()
+{
+ if (gp_cache_arena)
+ {
+ rtl_arena_destroy (gp_cache_arena);
+ gp_cache_arena = nullptr;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/alloc_cache.hxx b/sal/rtl/alloc_cache.hxx
new file mode 100644
index 000000000..501d5770b
--- /dev/null
+++ b/sal/rtl/alloc_cache.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_RTL_ALLOC_CACHE_HXX
+#define INCLUDED_SAL_RTL_ALLOC_CACHE_HXX
+
+#include <sal/types.h>
+#include <rtl/alloc.h>
+
+struct rtl_cache_st
+{
+ /* properties */
+ char m_name[RTL_CACHE_NAME_LENGTH + 1];
+
+ sal_Size m_type_size; /* const */
+
+ int (SAL_CALL * m_constructor)(void * obj, void * userarg); /* const */
+ void (SAL_CALL * m_destructor) (void * obj, void * userarg); /* const */
+ void * m_userarg;
+};
+
+#endif // INCLUDED_SAL_RTL_ALLOC_CACHE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/alloc_fini.cxx b/sal/rtl/alloc_fini.cxx
new file mode 100644
index 000000000..ba798452d
--- /dev/null
+++ b/sal/rtl/alloc_fini.cxx
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtllifecycle.h>
+
+namespace
+{
+ struct rtlCacheSingleton
+ {
+ rtlCacheSingleton()
+ {
+ rtl_cache_init();
+ }
+ ~rtlCacheSingleton()
+ {
+ rtl_cache_fini();
+ }
+ };
+ rtlCacheSingleton& theCacheSingleton()
+ {
+ static rtlCacheSingleton SINGLETON;
+ return SINGLETON;
+ }
+}
+
+void ensureCacheSingleton()
+{
+ theCacheSingleton();
+}
+
+namespace
+{
+ struct rtlArenaSingleton
+ {
+ rtlArenaSingleton()
+ {
+ rtl_arena_init();
+ }
+ ~rtlArenaSingleton()
+ {
+ rtl_arena_fini();
+ }
+ };
+ rtlArenaSingleton& theArenaSingleton()
+ {
+ static rtlArenaSingleton SINGLETON;
+ return SINGLETON;
+ }
+}
+
+void ensureArenaSingleton()
+{
+ theArenaSingleton();
+}
+
+namespace
+{
+ struct rtlLocaleSingleton
+ {
+ rtlLocaleSingleton()
+ {
+ rtl_locale_init();
+ }
+ ~rtlLocaleSingleton()
+ {
+ rtl_locale_fini();
+ }
+ };
+ rtlLocaleSingleton& theLocaleSingleton()
+ {
+ static rtlLocaleSingleton SINGLETON;
+ return SINGLETON;
+ }
+}
+
+void ensureLocaleSingleton()
+{
+ theLocaleSingleton();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/alloc_global.cxx b/sal/rtl/alloc_global.cxx
new file mode 100644
index 000000000..4b7ec6037
--- /dev/null
+++ b/sal/rtl/alloc_global.cxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "alloc_impl.hxx"
+#include <rtl/alloc.h>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+
+#include <algorithm>
+#include <cassert>
+#include <string.h>
+#include <stdio.h>
+
+#include <rtllifecycle.h>
+#include <oslmemory.h>
+
+void* SAL_CALL rtl_allocateMemory(sal_Size n) SAL_THROW_EXTERN_C()
+{
+ SAL_WARN_IF(
+ n >= SAL_MAX_INT32, "sal.rtl",
+ "suspicious massive alloc " << n);
+ return malloc (n);
+}
+
+void* SAL_CALL rtl_reallocateMemory(void * p, sal_Size n) SAL_THROW_EXTERN_C()
+{
+ SAL_WARN_IF(
+ n >= SAL_MAX_INT32, "sal.rtl",
+ "suspicious massive alloc " << n);
+ return realloc (p, n);
+}
+
+void SAL_CALL rtl_freeMemory(void * p) SAL_THROW_EXTERN_C()
+{
+ free (p);
+}
+
+void * SAL_CALL rtl_allocateZeroMemory(sal_Size n) SAL_THROW_EXTERN_C()
+{
+ SAL_WARN_IF( n >= SAL_MAX_INT32, "sal.rtl", "suspicious massive alloc " << n);
+ return calloc(n, 1);
+}
+
+void SAL_CALL rtl_freeZeroMemory(void * p, sal_Size n) SAL_THROW_EXTERN_C()
+{
+ if (p)
+ {
+ rtl_secureZeroMemory (p, n);
+ rtl_freeMemory (p);
+ }
+}
+
+void* SAL_CALL rtl_allocateAlignedMemory(sal_Size Alignment, sal_Size Bytes) SAL_THROW_EXTERN_C()
+{
+ return osl_aligned_alloc(Alignment, Bytes);
+}
+
+void SAL_CALL rtl_freeAlignedMemory(void* Ptr) SAL_THROW_EXTERN_C()
+{
+ osl_aligned_free(Ptr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/alloc_impl.hxx b/sal/rtl/alloc_impl.hxx
new file mode 100644
index 000000000..29c24cc99
--- /dev/null
+++ b/sal/rtl/alloc_impl.hxx
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_RTL_ALLOC_IMPL_HXX
+#define INCLUDED_SAL_RTL_ALLOC_IMPL_HXX
+
+#include <sal/types.h>
+
+/** Alignment macros
+ */
+#if SAL_TYPES_ALIGNMENT4 > 1
+#define RTL_MEMORY_ALIGNMENT_4 SAL_TYPES_ALIGNMENT4
+#else
+#define RTL_MEMORY_ALIGNMENT_4 sizeof(int)
+#endif /* SAL_TYPES_ALIGNMENT4 */
+
+#if SAL_TYPES_ALIGNMENT8 > 1
+#define RTL_MEMORY_ALIGNMENT_8 SAL_TYPES_ALIGNMENT8
+#else
+#define RTL_MEMORY_ALIGNMENT_8 sizeof(void*)
+#endif /* SAL_TYPES_ALIGNMENT8 */
+
+#if 0 /* @@@ */
+#define RTL_MEMORY_ALIGNMENT_1 8
+#define RTL_MEMORY_ALIGNMENT_2 (sizeof(void*) * 2)
+#endif /* @@@ */
+
+#define RTL_MEMORY_ALIGN(value, align) (((value) + ((align) - 1)) & ~((align) - 1))
+
+#define RTL_MEMORY_ISP2(value) (((value) & ((value) - 1)) == 0)
+#define RTL_MEMORY_P2ALIGN(value, align) ((value) & -static_cast<sal_IntPtr>(align))
+
+#define RTL_MEMORY_P2ROUNDUP(value, align) \
+ (-(-static_cast<sal_IntPtr>(value) & -static_cast<sal_IntPtr>(align)))
+#define RTL_MEMORY_P2END(value, align) \
+ (-(~static_cast<sal_IntPtr>(value) & -static_cast<sal_IntPtr>(align)))
+
+/** highbit(): log2() + 1
+ (complexity O(1))
+*/
+static inline unsigned int highbit(sal_Size n)
+{
+ unsigned int k = 1;
+
+ if (n == 0)
+ return 0;
+ if constexpr (sizeof(n) >= 8)
+ {
+ if (n & 0xffffffff00000000)
+ {
+ k |= 32;
+ n >>= 32;
+ }
+ }
+ if (n & 0xffff0000)
+ {
+ k |= 16;
+ n >>= 16;
+ }
+ if (n & 0xff00)
+ {
+ k |= 8;
+ n >>= 8;
+ }
+ if (n & 0xf0)
+ {
+ k |= 4;
+ n >>= 4;
+ }
+ if (n & 0x0c)
+ {
+ k |= 2;
+ n >>= 2;
+ }
+ if (n & 0x02)
+ k++;
+
+ return k;
+}
+
+/** find first bit set
+ (complexity O(1))
+*/
+static inline unsigned int lowbit(sal_Size n)
+{
+ unsigned int k = 1;
+
+ if (n == 0)
+ return 0;
+
+ if constexpr (sizeof(n) >= 8)
+ {
+ if (!(n & 0xffffffff))
+ {
+ k |= 32;
+ n >>= 32;
+ }
+ }
+
+ if (!(n & 0xffff))
+ {
+ k |= 16;
+ n >>= 16;
+ }
+
+ if (!(n & 0xff))
+ {
+ k |= 8;
+ n >>= 8;
+ }
+
+ if (!(n & 0xf))
+ {
+ k |= 4;
+ n >>= 4;
+ }
+
+ if (!(n & 0x3))
+ {
+ k |= 2;
+ n >>= 2;
+ }
+
+ if (!(n & 0x1))
+ k++;
+
+ return k;
+}
+
+/** Queue manipulation macros
+ (doubly linked circular list)
+ (complexity O(1))
+*/
+#define QUEUE_STARTED_NAMED(entry, name) \
+ (((entry)->m_##name##next == (entry)) && ((entry)->m_##name##prev == (entry)))
+
+#define QUEUE_START_NAMED(entry, name) \
+{ \
+ (entry)->m_##name##next = (entry); \
+ (entry)->m_##name##prev = (entry); \
+}
+
+#define QUEUE_REMOVE_NAMED(entry, name) \
+{ \
+ (entry)->m_##name##prev->m_##name##next = (entry)->m_##name##next; \
+ (entry)->m_##name##next->m_##name##prev = (entry)->m_##name##prev; \
+ QUEUE_START_NAMED(entry, name); \
+}
+
+#define QUEUE_INSERT_HEAD_NAMED(head, entry, name) \
+{ \
+ (entry)->m_##name##prev = (head); \
+ (entry)->m_##name##next = (head)->m_##name##next; \
+ (head)->m_##name##next = (entry); \
+ (entry)->m_##name##next->m_##name##prev = (entry); \
+}
+
+#define QUEUE_INSERT_TAIL_NAMED(head, entry, name) \
+{ \
+ (entry)->m_##name##next = (head); \
+ (entry)->m_##name##prev = (head)->m_##name##prev; \
+ (head)->m_##name##prev = (entry); \
+ (entry)->m_##name##prev->m_##name##next = (entry); \
+}
+
+#if defined(SAL_UNX)
+
+#include <unistd.h>
+#include <pthread.h>
+
+typedef pthread_mutex_t rtl_memory_lock_type;
+
+#define RTL_MEMORY_LOCK_INIT(lock) pthread_mutex_init((lock), nullptr)
+#define RTL_MEMORY_LOCK_DESTROY(lock) pthread_mutex_destroy((lock))
+
+#define RTL_MEMORY_LOCK_ACQUIRE(lock) pthread_mutex_lock((lock))
+#define RTL_MEMORY_LOCK_RELEASE(lock) pthread_mutex_unlock((lock))
+
+#elif defined(_WIN32)
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+typedef CRITICAL_SECTION rtl_memory_lock_type;
+
+#define RTL_MEMORY_LOCK_INIT(lock) InitializeCriticalSection((lock))
+#define RTL_MEMORY_LOCK_DESTROY(lock) DeleteCriticalSection((lock))
+
+#define RTL_MEMORY_LOCK_ACQUIRE(lock) EnterCriticalSection((lock))
+#define RTL_MEMORY_LOCK_RELEASE(lock) LeaveCriticalSection((lock))
+
+#else
+#error Unknown platform
+#endif /* SAL_UNX | _WIN32 */
+
+/** Cache creation flags.
+ @internal
+*/
+#define RTL_CACHE_FLAG_NOMAGAZINE (1 << 13) /* w/o magazine layer */
+
+#endif // INCLUDED_SAL_RTL_ALLOC_IMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/bootstrap.cxx b/sal/rtl/bootstrap.cxx
new file mode 100644
index 000000000..12558d319
--- /dev/null
+++ b/sal/rtl/bootstrap.cxx
@@ -0,0 +1,992 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <config_features.h>
+#include <config_folders.h>
+
+#include <rtl/bootstrap.h>
+#include <rtl/bootstrap.hxx>
+#include <osl/diagnose.h>
+#include <osl/module.h>
+#include <osl/process.h>
+#include <osl/file.hxx>
+#include <osl/mutex.hxx>
+#include <osl/profile.hxx>
+#include <osl/security.hxx>
+#include <rtl/alloc.h>
+#include <rtl/string.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/byteseq.hxx>
+#include <rtl/malformeduriexception.hxx>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+#include <o3tl/lru_map.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <utility>
+#include <vector>
+#include <algorithm>
+#include <cstddef>
+#include <string_view>
+#include <unordered_map>
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+#ifdef IOS
+#include <premac.h>
+#import <Foundation/Foundation.h>
+#include <postmac.h>
+#endif
+
+using osl::DirectoryItem;
+using osl::FileStatus;
+
+namespace
+{
+
+struct Bootstrap_Impl;
+
+constexpr std::u16string_view VND_SUN_STAR_PATHNAME = u"vnd.sun.star.pathname:";
+
+bool isPathnameUrl(std::u16string_view url)
+{
+ return o3tl::matchIgnoreAsciiCase(url, VND_SUN_STAR_PATHNAME);
+}
+
+bool resolvePathnameUrl(OUString * url)
+{
+ OSL_ASSERT(url);
+ if (!isPathnameUrl(*url) ||
+ (osl::FileBase::getFileURLFromSystemPath(
+ url->copy(VND_SUN_STAR_PATHNAME.size()), *url) ==
+ osl::FileBase::E_None))
+ {
+ return true;
+ }
+ *url = OUString();
+ return false;
+}
+
+enum LookupMode {
+ LOOKUP_MODE_NORMAL, LOOKUP_MODE_URE_BOOTSTRAP,
+ LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION };
+
+struct ExpandRequestLink {
+ ExpandRequestLink const * next;
+ Bootstrap_Impl const * file;
+ OUString key;
+};
+
+OUString expandMacros(
+ Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode,
+ ExpandRequestLink const * requestStack);
+
+OUString recursivelyExpandMacros(
+ Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode,
+ Bootstrap_Impl const * requestFile, OUString const & requestKey,
+ ExpandRequestLink const * requestStack)
+{
+ for (; requestStack; requestStack = requestStack->next) {
+ if (requestStack->file == requestFile &&
+ requestStack->key == requestKey)
+ {
+ return "***RECURSION DETECTED***";
+ }
+ }
+ ExpandRequestLink link = { requestStack, requestFile, requestKey };
+ return expandMacros(file, text, mode, &link);
+}
+
+struct rtl_bootstrap_NameValue
+{
+ OUString sName;
+ OUString sValue;
+
+ rtl_bootstrap_NameValue()
+ {}
+ rtl_bootstrap_NameValue(OUString name, OUString value )
+ : sName(std::move( name )),
+ sValue(std::move( value ))
+ {}
+};
+
+} // end namespace
+
+typedef std::vector<rtl_bootstrap_NameValue> NameValueVector;
+
+static bool find(
+ NameValueVector const & vector, OUString const & key,
+ OUString * value)
+{
+ OSL_ASSERT(value);
+ auto i = std::find_if(vector.begin(), vector.end(),
+ [&key](const rtl_bootstrap_NameValue& rNameValue) { return rNameValue.sName == key; });
+ if (i != vector.end())
+ {
+ *value = i->sValue;
+ return true;
+ }
+ return false;
+}
+
+namespace
+{
+ NameValueVector rtl_bootstrap_set_vector;
+}
+
+static bool getFromCommandLineArgs(
+ OUString const & key, OUString * value )
+{
+ OSL_ASSERT(value);
+
+ static NameValueVector nameValueVector = []()
+ {
+ NameValueVector tmp;
+
+ sal_Int32 nArgCount = osl_getCommandArgCount();
+ for(sal_Int32 i = 0; i < nArgCount; ++ i)
+ {
+ OUString pArg;
+ osl_getCommandArg( i, &pArg.pData );
+ if( (pArg.startsWith("-") || pArg.startsWith("/") ) &&
+ pArg.match("env:", 1) )
+ {
+ sal_Int32 nIndex = pArg.indexOf( '=' );
+
+ if( nIndex >= 0 )
+ {
+ rtl_bootstrap_NameValue nameValue;
+ nameValue.sName = pArg.copy( 5, nIndex - 5 );
+ nameValue.sValue = pArg.copy( nIndex+1 );
+
+ if( i == nArgCount-1 &&
+ nameValue.sValue.getLength() &&
+ nameValue.sValue[nameValue.sValue.getLength()-1] == 13 )
+ {
+ // avoid the 13 linefeed for the last argument,
+ // when the executable is started from a script,
+ // that was edited on windows
+ nameValue.sValue = nameValue.sValue.copy(0,nameValue.sValue.getLength()-1);
+ }
+
+ tmp.push_back( nameValue );
+ }
+ }
+ };
+ return tmp;
+ }();
+
+ return find(nameValueVector, key, value);
+}
+
+static void getExecutableDirectory_Impl(rtl_uString ** ppDirURL)
+{
+ OUString fileName;
+ osl_getExecutableFile(&(fileName.pData));
+
+ sal_Int32 nDirEnd = fileName.lastIndexOf('/');
+ OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory");
+
+ rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd);
+}
+
+static OUString & getIniFileName_Impl()
+{
+ static OUString aStaticName = []() {
+ OUString fileName;
+
+#if defined IOS
+ // On iOS hardcode the inifile as "rc" in the .app
+ // directory. Apps are self-contained anyway, there is no
+ // possibility to have several "applications" in the same
+ // installation location with different inifiles.
+ const char *inifile = [[@"vnd.sun.star.pathname:" stringByAppendingString: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"rc"]] UTF8String];
+ fileName = OUString(inifile, strlen(inifile), RTL_TEXTENCODING_UTF8);
+ resolvePathnameUrl(&fileName);
+#elif defined ANDROID
+ // Apps are self-contained on Android, too, can as well hardcode
+ // it as "rc" in the "/assets" directory, i.e. inside the app's
+ // .apk (zip) archive as the /assets/rc file.
+ fileName = OUString("vnd.sun.star.pathname:/assets/rc");
+ resolvePathnameUrl(&fileName);
+#elif defined(EMSCRIPTEN)
+ fileName = OUString("vnd.sun.star.pathname:/instdir/program/sofficerc");
+ resolvePathnameUrl(&fileName);
+#else
+ if (getFromCommandLineArgs("INIFILENAME", &fileName))
+ {
+ resolvePathnameUrl(&fileName);
+ }
+ else
+ {
+ osl_getExecutableFile(&(fileName.pData));
+
+ // get rid of a potential executable extension
+ OUString progExt = ".bin";
+ if (fileName.getLength() > progExt.getLength()
+ && o3tl::equalsIgnoreAsciiCase(fileName.subView(fileName.getLength() - progExt.getLength()), progExt))
+ {
+ fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
+ }
+
+ progExt = ".exe";
+ if (fileName.getLength() > progExt.getLength()
+ && o3tl::equalsIgnoreAsciiCase(fileName.subView(fileName.getLength() - progExt.getLength()), progExt))
+ {
+ fileName = fileName.copy(0, fileName.getLength() - progExt.getLength());
+ }
+
+ // append config file suffix
+ fileName += SAL_CONFIGFILE("");
+
+#ifdef MACOSX
+ // We keep only executables in the MacOS folder, and all
+ // rc files in LIBO_ETC_FOLDER (typically "Resources").
+ sal_Int32 off = fileName.lastIndexOf( "/MacOS/" );
+ if (off != -1)
+ fileName = fileName.replaceAt(off + 1, strlen("MacOS"), u"" LIBO_ETC_FOLDER);
+#endif
+ }
+#endif
+
+ return fileName;
+ }();
+
+ return aStaticName;
+}
+
+// ensure the given file url has no final slash
+
+static void EnsureNoFinalSlash (OUString & url)
+{
+ sal_Int32 i = url.getLength();
+
+ if (i > 0 && url[i - 1] == '/')
+ url = url.copy(0, i - 1);
+}
+
+namespace {
+
+struct Bootstrap_Impl
+{
+ sal_Int32 _nRefCount;
+ Bootstrap_Impl * _base_ini;
+
+ NameValueVector _nameValueVector;
+ OUString _iniName;
+
+ explicit Bootstrap_Impl (OUString const & rIniName);
+ ~Bootstrap_Impl();
+
+ static void * operator new (std::size_t n)
+ { return malloc (sal_uInt32(n)); }
+ static void operator delete (void * p , std::size_t)
+ { free (p); }
+
+ bool getValue(
+ OUString const & key, rtl_uString ** value,
+ rtl_uString * defaultValue, LookupMode mode, bool override,
+ ExpandRequestLink const * requestStack) const;
+ bool getDirectValue(
+ OUString const & key, rtl_uString ** value, LookupMode mode,
+ ExpandRequestLink const * requestStack) const;
+ bool getAmbienceValue(
+ OUString const & key, rtl_uString ** value, LookupMode mode,
+ ExpandRequestLink const * requestStack) const;
+ void expandValue(
+ rtl_uString ** value, OUString const & text, LookupMode mode,
+ Bootstrap_Impl const * requestFile, OUString const & requestKey,
+ ExpandRequestLink const * requestStack) const;
+};
+
+}
+
+Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName )
+ : _nRefCount( 0 ),
+ _base_ini( nullptr ),
+ _iniName (rIniName)
+{
+ OUString base_ini(getIniFileName_Impl());
+ // normalize path
+ FileStatus status( osl_FileStatus_Mask_FileURL );
+ DirectoryItem dirItem;
+ if (DirectoryItem::get(base_ini, dirItem) == DirectoryItem::E_None &&
+ dirItem.getFileStatus(status) == DirectoryItem::E_None)
+ {
+ base_ini = status.getFileURL();
+ if (rIniName != base_ini)
+ {
+ _base_ini = static_cast< Bootstrap_Impl * >(
+ rtl_bootstrap_args_open(base_ini.pData));
+ }
+ }
+ SAL_INFO("sal.bootstrap", "Bootstrap_Impl(): sFile=" << _iniName);
+ oslFileHandle handle;
+ if (!_iniName.isEmpty() &&
+ osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read) == osl_File_E_None)
+ {
+ rtl::ByteSequence seq;
+
+ while (osl_readLine(handle , reinterpret_cast<sal_Sequence **>(&seq)) == osl_File_E_None)
+ {
+ OString line(reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength());
+ sal_Int32 nIndex = line.indexOf('=');
+ if (nIndex >= 1)
+ {
+ struct rtl_bootstrap_NameValue nameValue;
+ nameValue.sName = OStringToOUString(o3tl::trim(line.subView(0,nIndex)), RTL_TEXTENCODING_ASCII_US);
+ nameValue.sValue = OStringToOUString(o3tl::trim(line.subView(nIndex+1)), RTL_TEXTENCODING_UTF8);
+
+ SAL_INFO("sal.bootstrap", "pushing: name=" << nameValue.sName << " value=" << nameValue.sValue);
+
+ _nameValueVector.push_back(nameValue);
+ }
+ }
+ osl_closeFile(handle);
+ }
+ else
+ {
+ SAL_INFO( "sal.bootstrap", "couldn't open file: " << _iniName );
+ }
+}
+
+Bootstrap_Impl::~Bootstrap_Impl()
+{
+ if (_base_ini)
+ rtl_bootstrap_args_close( _base_ini );
+}
+
+namespace {
+
+Bootstrap_Impl * get_static_bootstrap_handle()
+{
+ static Bootstrap_Impl* s_handle = []() {
+ OUString iniName(getIniFileName_Impl());
+ Bootstrap_Impl* that = static_cast<Bootstrap_Impl*>(rtl_bootstrap_args_open(iniName.pData));
+ if (!that)
+ {
+ that = new Bootstrap_Impl(iniName);
+ ++that->_nRefCount;
+ }
+ return that;
+ }();
+
+ return s_handle;
+}
+
+struct FundamentalIniData
+{
+ rtlBootstrapHandle ini;
+
+ FundamentalIniData()
+ {
+ OUString uri;
+ ini =
+ (get_static_bootstrap_handle()->getValue(
+ "URE_BOOTSTRAP", &uri.pData, nullptr, LOOKUP_MODE_NORMAL, false,
+ nullptr)
+ && resolvePathnameUrl(&uri))
+ ? rtl_bootstrap_args_open(uri.pData) : nullptr;
+ }
+
+ ~FundamentalIniData() { rtl_bootstrap_args_close(ini); }
+
+ FundamentalIniData(const FundamentalIniData&) = delete;
+ FundamentalIniData& operator=(const FundamentalIniData&) = delete;
+};
+
+FundamentalIniData& FundamentalIni()
+{
+ static FundamentalIniData SINGLETON;
+ return SINGLETON;
+}
+
+}
+
+bool Bootstrap_Impl::getValue(
+ OUString const & key, rtl_uString ** value, rtl_uString * defaultValue,
+ LookupMode mode, bool override, ExpandRequestLink const * requestStack)
+ const
+{
+ if (mode == LOOKUP_MODE_NORMAL && key == "URE_BOOTSTRAP")
+ mode = LOOKUP_MODE_URE_BOOTSTRAP;
+
+ if (override && getDirectValue(key, value, mode, requestStack))
+ return true;
+
+ if (key == "_OS")
+ {
+ rtl_uString_assign(
+ value, OUString(RTL_OS).pData);
+ return true;
+ }
+
+ if (key == "_ARCH")
+ {
+ rtl_uString_assign(
+ value, OUString(RTL_ARCH).pData);
+ return true;
+ }
+
+ if (key == "_CPPU_ENV")
+ {
+ rtl_uString_assign(
+ value,
+ (OUString(
+ SAL_STRINGIFY(CPPU_ENV)).
+ pData));
+ return true;
+ }
+
+#ifdef ANDROID
+ if (key == "APP_DATA_DIR")
+ {
+ const char *app_data_dir = lo_get_app_data_dir();
+ rtl_uString_assign(
+ value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
+ return true;
+ }
+#endif
+
+#ifdef IOS
+ if (key == "APP_DATA_DIR")
+ {
+ const char *app_data_dir = [[[[NSBundle mainBundle] bundlePath] stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLPathAllowedCharacterSet]] UTF8String];
+ rtl_uString_assign(
+ value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData);
+ return true;
+ }
+#endif
+
+ if (key == "ORIGIN")
+ {
+ rtl_uString_assign(
+ value,
+ _iniName.copy(
+ 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData);
+ return true;
+ }
+
+ if (getAmbienceValue(key, value, mode, requestStack))
+ return true;
+
+ if (key == "SYSUSERCONFIG")
+ {
+ OUString v;
+ bool b = osl::Security().getConfigDir(v);
+ EnsureNoFinalSlash(v);
+ rtl_uString_assign(value, v.pData);
+ return b;
+ }
+
+ if (key == "SYSUSERHOME")
+ {
+ OUString v;
+ bool b = osl::Security().getHomeDir(v);
+ EnsureNoFinalSlash(v);
+ rtl_uString_assign(value, v.pData);
+ return b;
+ }
+
+ if (key == "SYSBINDIR")
+ {
+ getExecutableDirectory_Impl(value);
+ return true;
+ }
+
+ if (_base_ini != nullptr && _base_ini->getDirectValue(key, value, mode, requestStack))
+ return true;
+
+ if (!override && getDirectValue(key, value, mode, requestStack))
+ return true;
+
+ if (mode == LOOKUP_MODE_NORMAL)
+ {
+ FundamentalIniData const & d = FundamentalIni();
+ Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini);
+ if (b != nullptr && b != this && b->getDirectValue(key, value, mode, requestStack))
+ return true;
+ }
+
+ if (defaultValue != nullptr)
+ {
+ rtl_uString_assign(value, defaultValue);
+ return true;
+ }
+
+ rtl_uString_new(value);
+ return false;
+}
+
+bool Bootstrap_Impl::getDirectValue(
+ OUString const & key, rtl_uString ** value, LookupMode mode,
+ ExpandRequestLink const * requestStack) const
+{
+ OUString v;
+ if (find(_nameValueVector, key, &v))
+ {
+ expandValue(value, v, mode, this, key, requestStack);
+ return true;
+ }
+
+ return false;
+}
+
+bool Bootstrap_Impl::getAmbienceValue(
+ OUString const & key, rtl_uString ** value, LookupMode mode,
+ ExpandRequestLink const * requestStack) const
+{
+ OUString v;
+ bool f;
+
+ {
+ osl::MutexGuard g(osl::Mutex::getGlobalMutex());
+ f = find(rtl_bootstrap_set_vector, key, &v);
+ }
+
+ if (f || getFromCommandLineArgs(key, &v) ||
+ osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None)
+ {
+ expandValue(value, v, mode, nullptr, key, requestStack);
+ return true;
+ }
+
+ return false;
+}
+
+void Bootstrap_Impl::expandValue(
+ rtl_uString ** value, OUString const & text, LookupMode mode,
+ Bootstrap_Impl const * requestFile, OUString const & requestKey,
+ ExpandRequestLink const * requestStack) const
+{
+ rtl_uString_assign(
+ value,
+ (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ?
+ text :
+ recursivelyExpandMacros(
+ this, text,
+ (mode == LOOKUP_MODE_URE_BOOTSTRAP ?
+ LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode),
+ requestFile, requestKey, requestStack)).pData);
+}
+
+namespace {
+
+typedef std::unordered_map< OUString, Bootstrap_Impl * > bootstrap_map_t;
+bootstrap_map_t bootstrap_map;
+
+}
+
+rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open(rtl_uString * pIniName)
+{
+ static o3tl::lru_map<OUString,OUString> fileUrlLookupCache(16);
+
+ OUString originalIniName( pIniName );
+ OUString iniName;
+
+ osl::ResettableMutexGuard guard(osl::Mutex::getGlobalMutex());
+ auto cacheIt = fileUrlLookupCache.find(originalIniName);
+ bool foundInCache = cacheIt != fileUrlLookupCache.end();
+ if (foundInCache)
+ iniName = cacheIt->second;
+ guard.clear();
+
+ // normalize path
+ if (!foundInCache)
+ {
+ FileStatus status(osl_FileStatus_Mask_FileURL);
+ DirectoryItem dirItem;
+ if (DirectoryItem::get(originalIniName, dirItem) != DirectoryItem::E_None ||
+ dirItem.getFileStatus(status) != DirectoryItem::E_None)
+ {
+ return nullptr;
+ }
+ iniName = status.getFileURL();
+ }
+
+ guard.reset();
+ if (!foundInCache)
+ fileUrlLookupCache.insert({originalIniName, iniName});
+ Bootstrap_Impl * that;
+ auto iFind(bootstrap_map.find(iniName));
+ if (iFind == bootstrap_map.end())
+ {
+ guard.clear();
+ that = new Bootstrap_Impl(iniName);
+ guard.reset();
+ iFind = bootstrap_map.find(iniName);
+ if (iFind == bootstrap_map.end())
+ {
+ ++that->_nRefCount;
+ ::std::pair< bootstrap_map_t::iterator, bool > insertion(
+ bootstrap_map.emplace(iniName, that));
+ OSL_ASSERT(insertion.second);
+ }
+ else
+ {
+ Bootstrap_Impl * obsolete = that;
+ that = iFind->second;
+ ++that->_nRefCount;
+ guard.clear();
+ delete obsolete;
+ }
+ }
+ else
+ {
+ that = iFind->second;
+ ++that->_nRefCount;
+ }
+ return static_cast< rtlBootstrapHandle >( that );
+}
+
+void SAL_CALL rtl_bootstrap_args_close(rtlBootstrapHandle handle) SAL_THROW_EXTERN_C()
+{
+ if (!handle)
+ return;
+
+ Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle );
+
+ osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
+ OSL_ASSERT(bootstrap_map.find(that->_iniName)->second == that);
+ --that->_nRefCount;
+
+ if (that->_nRefCount != 0)
+ return;
+
+ std::size_t const nLeaking = 8; // only hold up to 8 files statically
+ if (bootstrap_map.size() > nLeaking)
+ {
+ ::std::size_t erased = bootstrap_map.erase( that->_iniName );
+ if (erased != 1) {
+ OSL_ASSERT( false );
+ }
+ delete that;
+ }
+}
+
+sal_Bool SAL_CALL rtl_bootstrap_get_from_handle(
+ rtlBootstrapHandle handle,
+ rtl_uString * pName,
+ rtl_uString ** ppValue,
+ rtl_uString * pDefault
+)
+{
+ osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
+
+ bool found = false;
+ if(ppValue && pName)
+ {
+ if (!handle)
+ handle = get_static_bootstrap_handle();
+
+ found = static_cast< Bootstrap_Impl * >(handle)->getValue(
+ pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, nullptr );
+ }
+
+ return found;
+}
+
+void SAL_CALL rtl_bootstrap_get_iniName_from_handle (
+ rtlBootstrapHandle handle,
+ rtl_uString ** ppIniName
+)
+{
+ if(!ppIniName)
+ return;
+
+ if(handle)
+ {
+ Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle);
+ rtl_uString_assign(ppIniName, pImpl->_iniName.pData);
+ }
+ else
+ {
+ const OUString & iniName = getIniFileName_Impl();
+ rtl_uString_assign(ppIniName, iniName.pData);
+ }
+}
+
+void SAL_CALL rtl_bootstrap_setIniFileName (
+ rtl_uString * pName
+)
+{
+ osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
+ OUString & file = getIniFileName_Impl();
+ file = pName;
+}
+
+sal_Bool SAL_CALL rtl_bootstrap_get (
+ rtl_uString * pName,
+ rtl_uString ** ppValue,
+ rtl_uString * pDefault
+)
+{
+ return rtl_bootstrap_get_from_handle(nullptr, pName, ppValue, pDefault);
+}
+
+void SAL_CALL rtl_bootstrap_set (
+ rtl_uString * pName,
+ rtl_uString * pValue
+)
+{
+ const OUString name(pName);
+ const OUString value(pValue);
+
+ osl::MutexGuard guard(osl::Mutex::getGlobalMutex());
+
+ for (auto & item : rtl_bootstrap_set_vector)
+ {
+ if (item.sName == name)
+ {
+ item.sValue = value;
+ return;
+ }
+ }
+
+ SAL_INFO("sal.bootstrap", "explicitly getting: name=" << name << " value=" <<value);
+
+ rtl_bootstrap_set_vector.emplace_back(name, value);
+}
+
+void SAL_CALL rtl_bootstrap_expandMacros_from_handle(
+ rtlBootstrapHandle handle,
+ rtl_uString ** macro)
+{
+ if (!handle)
+ handle = get_static_bootstrap_handle();
+
+ OUString expanded(expandMacros(static_cast< Bootstrap_Impl * >(handle),
+ OUString::unacquired(macro),
+ LOOKUP_MODE_NORMAL, nullptr));
+ rtl_uString_assign(macro, expanded.pData);
+}
+
+void SAL_CALL rtl_bootstrap_expandMacros(rtl_uString ** macro)
+{
+ rtl_bootstrap_expandMacros_from_handle(nullptr, macro);
+}
+
+void rtl_bootstrap_encode(rtl_uString const * value, rtl_uString ** encoded)
+{
+ OSL_ASSERT(value);
+ OUStringBuffer b(value->length+5);
+ for (sal_Int32 i = 0; i < value->length; ++i)
+ {
+ sal_Unicode c = value->buffer[i];
+ if (c == '$' || c == '\\')
+ b.append('\\');
+
+ b.append(c);
+ }
+
+ rtl_uString_assign(encoded, b.makeStringAndClear().pData);
+}
+
+namespace {
+
+int hex(sal_Unicode c)
+{
+ return
+ c >= '0' && c <= '9' ? c - '0' :
+ c >= 'A' && c <= 'F' ? c - 'A' + 10 :
+ c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
+}
+
+sal_Unicode read(std::u16string_view text, std::size_t * pos, bool * escaped)
+{
+ OSL_ASSERT(pos && *pos < text.length() && escaped);
+ sal_Unicode c = text[(*pos)++];
+ if (c == '\\')
+ {
+ int n1, n2, n3, n4;
+ if (*pos < text.length() - 4 && text[*pos] == 'u' &&
+ ((n1 = hex(text[*pos + 1])) >= 0) &&
+ ((n2 = hex(text[*pos + 2])) >= 0) &&
+ ((n3 = hex(text[*pos + 3])) >= 0) &&
+ ((n4 = hex(text[*pos + 4])) >= 0))
+ {
+ *pos += 5;
+ *escaped = true;
+ return static_cast< sal_Unicode >(
+ (n1 << 12) | (n2 << 8) | (n3 << 4) | n4);
+ }
+
+ if (*pos < text.length())
+ {
+ *escaped = true;
+ return text[(*pos)++];
+ }
+ }
+
+ *escaped = false;
+ return c;
+}
+
+OUString lookup(
+ Bootstrap_Impl const * file, LookupMode mode, bool override,
+ OUString const & key, ExpandRequestLink const * requestStack)
+{
+ OUString v;
+ (file == nullptr ? get_static_bootstrap_handle() : file)->getValue(
+ key, &v.pData, nullptr, mode, override, requestStack);
+ return v;
+}
+
+OUString expandMacros(
+ Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode,
+ ExpandRequestLink const * requestStack)
+{
+ SAL_INFO("sal.bootstrap", "expandMacros called with: " << OUString(text));
+ OUStringBuffer buf(2048);
+
+ for (std::size_t i = 0; i < text.length();)
+ {
+ bool escaped;
+ sal_Unicode c = read(text, &i, &escaped);
+ if (escaped || c != '$')
+ {
+ buf.append(c);
+ }
+ else
+ {
+ if (i < text.length() && text[i] == '{')
+ {
+ ++i;
+ std::size_t p = i;
+ sal_Int32 nesting = 0;
+ OUString seg[3];
+ int n = 0;
+
+ while (i < text.length())
+ {
+ std::size_t j = i;
+ c = read(text, &i, &escaped);
+
+ if (!escaped)
+ {
+ switch (c)
+ {
+ case '{':
+ ++nesting;
+ break;
+ case '}':
+ if (nesting == 0)
+ {
+ seg[n++] = text.substr(p, j - p);
+ goto done;
+ }
+ else
+ {
+ --nesting;
+ }
+ break;
+ case ':':
+ if (nesting == 0 && n < 2)
+ {
+ seg[n++] = text.substr(p, j - p);
+ p = i;
+ }
+ break;
+ }
+ }
+ }
+ done:
+ for (int j = 0; j < n; ++j)
+ {
+ seg[j] = expandMacros(file, seg[j], mode, requestStack);
+ }
+
+ if (n == 3 && seg[0] != ".override" && seg[1].isEmpty())
+ {
+ // For backward compatibility, treat ${file::key} the
+ // same as just ${file:key}:
+ seg[1] = seg[2];
+ n = 2;
+ }
+
+ if (n == 1)
+ {
+ buf.append(lookup(file, mode, false, seg[0], requestStack));
+ }
+ else if (n == 2)
+ {
+ rtl::Bootstrap b(seg[0]);
+ Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle());
+ buf.append(lookup(f, mode, false, seg[1], requestStack));
+ }
+ else if (n == 3 && seg[0] == ".override")
+ {
+ rtl::Bootstrap b(seg[1]);
+ Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle());
+ buf.append(lookup(f, mode, f != nullptr, seg[2], requestStack));
+ }
+ else
+ {
+ // Going through osl::Profile, this code erroneously
+ // does not recursively expand macros in the resulting
+ // replacement text (and if it did, it would fail to
+ // detect cycles that pass through here):
+ buf.append(
+ OStringToOUString(
+ osl::Profile(seg[0]).readString(
+ OUStringToOString(
+ seg[1], RTL_TEXTENCODING_UTF8),
+ OUStringToOString(
+ seg[2], RTL_TEXTENCODING_UTF8),
+ OString()),
+ RTL_TEXTENCODING_UTF8));
+ }
+ }
+ else
+ {
+ OUStringBuffer kbuf(sal_Int32(text.length()));
+ for (; i < text.length();)
+ {
+ std::size_t j = i;
+ c = read(text, &j, &escaped);
+ if (!escaped &&
+ (c == ' ' || c == '$' || c == '-' || c == '/' ||
+ c == ';' || c == '\\'))
+ {
+ break;
+ }
+
+ kbuf.append(c);
+ i = j;
+ }
+
+ buf.append(
+ lookup(
+ file, mode, false, kbuf.makeStringAndClear(),
+ requestStack));
+ }
+ }
+ }
+
+ OUString result(buf.makeStringAndClear());
+ SAL_INFO("sal.bootstrap", "expandMacros result: " << result);
+
+ return result;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/byteseq.cxx b/sal/rtl/byteseq.cxx
new file mode 100644
index 000000000..3c4967d54
--- /dev/null
+++ b/sal/rtl/byteseq.cxx
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <osl/diagnose.h>
+#include <osl/interlck.h>
+
+#include <rtl/byteseq.h>
+
+/* static data to be referenced by all empty strings
+ * the refCount is predefined to 1 and must never become 0 !
+ */
+static sal_Sequence aEmpty_rtl_ByteSeq =
+{
+ 1, /* sal_Int32 refCount; */
+ 0, /* sal_Int32 length; */
+ { 0 } /* sal_Unicode buffer[1]; */
+};
+
+void SAL_CALL rtl_byte_sequence_reference2One(
+ sal_Sequence ** ppSequence ) SAL_THROW_EXTERN_C()
+{
+ sal_Sequence * pSequence;
+
+ OSL_ENSURE( ppSequence, "### null ptr!" );
+ pSequence = *ppSequence;
+
+ if (pSequence->nRefCount > 1)
+ {
+ sal_Sequence *pNew;
+ sal_Int32 nElements = pSequence->nElements;
+ if (nElements)
+ {
+ pNew = static_cast<sal_Sequence *>(malloc( SAL_SEQUENCE_HEADER_SIZE + nElements ));
+
+ if ( pNew != nullptr )
+ memcpy( pNew->elements, pSequence->elements, nElements );
+
+ if (! osl_atomic_decrement( &pSequence->nRefCount ))
+ free( pSequence );
+ }
+ else
+ {
+ pNew = static_cast<sal_Sequence *>(malloc( SAL_SEQUENCE_HEADER_SIZE ));
+ }
+
+ if ( pNew != nullptr )
+ {
+ pNew->nRefCount = 1;
+ pNew->nElements = nElements;
+ }
+
+ *ppSequence = pNew;
+ }
+}
+
+void SAL_CALL rtl_byte_sequence_realloc(
+ sal_Sequence ** ppSequence, sal_Int32 nSize ) SAL_THROW_EXTERN_C()
+{
+ sal_Sequence * pSequence;
+ sal_Int32 nElements;
+
+ assert(ppSequence && "### null ptr!");
+ pSequence = *ppSequence;
+ nElements = pSequence->nElements;
+
+ if (nElements == nSize)
+ return;
+
+ if (pSequence->nRefCount > 1) // split
+ {
+ sal_Sequence *pNew = static_cast<sal_Sequence *>(malloc( SAL_SEQUENCE_HEADER_SIZE + nSize ));
+
+ if ( pNew != nullptr )
+ {
+ if (nSize > nElements)
+ {
+ memcpy( pNew->elements, pSequence->elements, nElements );
+ memset( pNew->elements + nElements, 0, nSize - nElements );
+ }
+ else
+ {
+ memcpy( pNew->elements, pSequence->elements, nSize );
+ }
+ }
+
+ if (! osl_atomic_decrement( &pSequence->nRefCount ))
+ free( pSequence );
+ pSequence = pNew;
+ }
+ else
+ {
+ pSequence = static_cast<sal_Sequence *>(realloc(
+ pSequence, SAL_SEQUENCE_HEADER_SIZE + nSize ));
+ }
+
+ if ( pSequence != nullptr )
+ {
+ pSequence->nRefCount = 1;
+ pSequence->nElements = nSize;
+ }
+
+ *ppSequence = pSequence;
+}
+
+void SAL_CALL rtl_byte_sequence_acquire( sal_Sequence *pSequence )
+ SAL_THROW_EXTERN_C()
+{
+ OSL_ASSERT( pSequence );
+ osl_atomic_increment( &(pSequence->nRefCount) );
+}
+
+void SAL_CALL rtl_byte_sequence_release( sal_Sequence *pSequence )
+ SAL_THROW_EXTERN_C()
+{
+ if ( pSequence != nullptr )
+ {
+ if (! osl_atomic_decrement( &(pSequence->nRefCount )) )
+ {
+ free( pSequence );
+ }
+ }
+}
+
+void SAL_CALL rtl_byte_sequence_construct( sal_Sequence **ppSequence , sal_Int32 nLength )
+ SAL_THROW_EXTERN_C()
+{
+ OSL_ASSERT( ppSequence );
+ if( *ppSequence )
+ {
+ rtl_byte_sequence_release( *ppSequence );
+ *ppSequence = nullptr;
+ }
+
+ if( nLength )
+ {
+ *ppSequence = static_cast<sal_Sequence *>(rtl_allocateZeroMemory( SAL_SEQUENCE_HEADER_SIZE + nLength ));
+
+ if ( *ppSequence != nullptr )
+ {
+ (*ppSequence)->nRefCount = 1;
+ (*ppSequence)->nElements = nLength;
+ }
+ }
+ else
+ {
+ *ppSequence = &aEmpty_rtl_ByteSeq;
+ rtl_byte_sequence_acquire( *ppSequence );
+ }
+}
+
+void SAL_CALL rtl_byte_sequence_constructNoDefault( sal_Sequence **ppSequence , sal_Int32 nLength )
+ SAL_THROW_EXTERN_C()
+{
+ OSL_ASSERT( ppSequence );
+ if( *ppSequence )
+ {
+ rtl_byte_sequence_release( *ppSequence );
+ *ppSequence = nullptr;
+ }
+
+ *ppSequence = static_cast<sal_Sequence *>(malloc( SAL_SEQUENCE_HEADER_SIZE + nLength ));
+
+ if ( *ppSequence != nullptr )
+ {
+ (*ppSequence)->nRefCount = 1;
+ (*ppSequence)->nElements = nLength;
+ }
+}
+
+void SAL_CALL rtl_byte_sequence_constructFromArray(
+ sal_Sequence **ppSequence, const sal_Int8 *pData , sal_Int32 nLength )
+ SAL_THROW_EXTERN_C()
+{
+ rtl_byte_sequence_constructNoDefault( ppSequence , nLength );
+ if ( *ppSequence != nullptr && nLength != 0 )
+ memcpy( (*ppSequence)->elements, pData, nLength );
+}
+
+void SAL_CALL rtl_byte_sequence_assign( sal_Sequence **ppSequence , sal_Sequence *pSequence )
+ SAL_THROW_EXTERN_C()
+{
+ if ( *ppSequence != pSequence)
+ {
+ if( *ppSequence )
+ {
+ rtl_byte_sequence_release( *ppSequence );
+ }
+ *ppSequence = pSequence;
+ rtl_byte_sequence_acquire( *ppSequence );
+ }
+// else
+// nothing to do
+
+}
+
+sal_Bool SAL_CALL rtl_byte_sequence_equals( sal_Sequence *pSequence1 , sal_Sequence *pSequence2 )
+ SAL_THROW_EXTERN_C()
+{
+ assert(pSequence1 && pSequence2);
+ if (pSequence1 == pSequence2)
+ {
+ return true;
+ }
+ if (pSequence1->nElements != pSequence2->nElements)
+ {
+ return false;
+ }
+ return
+ memcmp(
+ pSequence1->elements, pSequence2->elements, pSequence1->nElements )
+ == 0;
+}
+
+const sal_Int8 *SAL_CALL rtl_byte_sequence_getConstArray( sal_Sequence *pSequence )
+ SAL_THROW_EXTERN_C()
+{
+ return reinterpret_cast<sal_Int8*>(pSequence->elements);
+}
+
+sal_Int32 SAL_CALL rtl_byte_sequence_getLength( sal_Sequence *pSequence )
+ SAL_THROW_EXTERN_C()
+{
+ return pSequence->nElements;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/cipher.cxx b/sal/rtl/cipher.cxx
new file mode 100644
index 000000000..bb0884b7a
--- /dev/null
+++ b/sal/rtl/cipher.cxx
@@ -0,0 +1,1433 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+
+#include <o3tl/safeint.hxx>
+#include <sal/types.h>
+#include <rtl/alloc.h>
+#include <rtl/cipher.h>
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <limits>
+
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+#include <openssl/evp.h>
+#endif
+
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
+#define RTL_CIPHER_NTOHL(c, l) \
+ ((l) = (static_cast<sal_uInt32>(*((c)++))) << 24, \
+ (l) |= (static_cast<sal_uInt32>(*((c)++))) << 16, \
+ (l) |= (static_cast<sal_uInt32>(*((c)++))) << 8, \
+ (l) |= (static_cast<sal_uInt32>(*((c)++))))
+
+#define RTL_CIPHER_HTONL(l, c) \
+ (*((c)++) = static_cast<sal_uInt8>(((l) >> 24) & 0xff), \
+ *((c)++) = static_cast<sal_uInt8>(((l) >> 16) & 0xff), \
+ *((c)++) = static_cast<sal_uInt8>(((l) >> 8) & 0xff), \
+ *((c)++) = static_cast<sal_uInt8>(((l) ) & 0xff))
+
+#define RTL_CIPHER_NTOHL64(c, xl, xr, n) \
+{ \
+ (xl) = (xr) = 0; \
+ (c) += (n); \
+ switch ((n)) \
+ { \
+ case 8: (xr) = (static_cast<sal_uInt32>(*(--(c)))); \
+ [[fallthrough]]; \
+ case 7: (xr) |= (static_cast<sal_uInt32>(*(--(c)))) << 8; \
+ [[fallthrough]]; \
+ case 6: (xr) |= (static_cast<sal_uInt32>(*(--(c)))) << 16; \
+ [[fallthrough]]; \
+ case 5: (xr) |= (static_cast<sal_uInt32>(*(--(c)))) << 24; \
+ [[fallthrough]]; \
+ case 4: (xl) = (static_cast<sal_uInt32>(*(--(c)))); \
+ [[fallthrough]]; \
+ case 3: (xl) |= (static_cast<sal_uInt32>(*(--(c)))) << 8; \
+ [[fallthrough]]; \
+ case 2: (xl) |= (static_cast<sal_uInt32>(*(--(c)))) << 16; \
+ [[fallthrough]]; \
+ case 1: (xl) |= (static_cast<sal_uInt32>(*(--(c)))) << 24; \
+ } \
+}
+
+#define RTL_CIPHER_HTONL64(xl, xr, c, n) \
+{ \
+ (c) += (n); \
+ switch ((n)) \
+ { \
+ case 8: *(--(c)) = static_cast<sal_uInt8>(((xr) ) & 0xff); \
+ [[fallthrough]]; \
+ case 7: *(--(c)) = static_cast<sal_uInt8>(((xr) >> 8) & 0xff); \
+ [[fallthrough]]; \
+ case 6: *(--(c)) = static_cast<sal_uInt8>(((xr) >> 16) & 0xff); \
+ [[fallthrough]]; \
+ case 5: *(--(c)) = static_cast<sal_uInt8>(((xr) >> 24) & 0xff); \
+ [[fallthrough]]; \
+ case 4: *(--(c)) = static_cast<sal_uInt8>(((xl) ) & 0xff); \
+ [[fallthrough]]; \
+ case 3: *(--(c)) = static_cast<sal_uInt8>(((xl) >> 8) & 0xff); \
+ [[fallthrough]]; \
+ case 2: *(--(c)) = static_cast<sal_uInt8>(((xl) >> 16) & 0xff); \
+ [[fallthrough]]; \
+ case 1: *(--(c)) = static_cast<sal_uInt8>(((xl) >> 24) & 0xff); \
+ } \
+}
+#endif
+
+typedef rtlCipherError(cipher_init_t) (
+ rtlCipher Cipher,
+ rtlCipherDirection Direction,
+ const sal_uInt8 *pKeyData, sal_Size nKeyLen,
+ const sal_uInt8 *pArgData, sal_Size nArgLen);
+
+typedef rtlCipherError(cipher_update_t) (
+ rtlCipher Cipher,
+ const void *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen);
+
+typedef void (cipher_delete_t) (rtlCipher Cipher);
+
+namespace {
+
+struct Cipher_Impl
+{
+ rtlCipherAlgorithm m_algorithm;
+ rtlCipherDirection m_direction;
+ rtlCipherMode m_mode;
+
+ cipher_init_t *m_init;
+ cipher_update_t *m_encode;
+ cipher_update_t *m_decode;
+ cipher_delete_t *m_delete;
+};
+
+}
+
+rtlCipher SAL_CALL rtl_cipher_create(
+ rtlCipherAlgorithm Algorithm,
+ rtlCipherMode Mode) SAL_THROW_EXTERN_C()
+{
+ rtlCipher Cipher = nullptr;
+ switch (Algorithm)
+ {
+ case rtl_Cipher_AlgorithmBF:
+ Cipher = rtl_cipher_createBF (Mode);
+ break;
+
+ case rtl_Cipher_AlgorithmARCFOUR:
+ Cipher = rtl_cipher_createARCFOUR (Mode);
+ break;
+
+ default: /* rtl_Cipher_AlgorithmInvalid */
+ break;
+ }
+ return Cipher;
+}
+
+rtlCipherError SAL_CALL rtl_cipher_init(
+ rtlCipher Cipher,
+ rtlCipherDirection Direction,
+ const sal_uInt8 *pKeyData, sal_Size nKeyLen,
+ const sal_uInt8 *pArgData, sal_Size nArgLen) SAL_THROW_EXTERN_C()
+{
+ Cipher_Impl *pImpl = static_cast<Cipher_Impl*>(Cipher);
+ if (!pImpl)
+ return rtl_Cipher_E_Argument;
+
+ if (!pImpl->m_init)
+ return rtl_Cipher_E_Unknown;
+
+ return (pImpl->m_init)(
+ Cipher, Direction, pKeyData, nKeyLen, pArgData, nArgLen);
+}
+
+rtlCipherError SAL_CALL rtl_cipher_encode(
+ rtlCipher Cipher,
+ const void *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen) SAL_THROW_EXTERN_C()
+{
+ Cipher_Impl *pImpl = static_cast<Cipher_Impl*>(Cipher);
+ if (!pImpl)
+ return rtl_Cipher_E_Argument;
+
+ if (!pImpl->m_encode)
+ return rtl_Cipher_E_Unknown;
+
+ return (pImpl->m_encode)(Cipher, pData, nDatLen, pBuffer, nBufLen);
+}
+
+rtlCipherError SAL_CALL rtl_cipher_decode(
+ rtlCipher Cipher,
+ const void *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen) SAL_THROW_EXTERN_C()
+{
+ Cipher_Impl *pImpl = static_cast<Cipher_Impl*>(Cipher);
+ if (!pImpl)
+ return rtl_Cipher_E_Argument;
+
+ if (!pImpl->m_decode)
+ return rtl_Cipher_E_Unknown;
+
+ return (pImpl->m_decode)(Cipher, pData, nDatLen, pBuffer, nBufLen);
+}
+
+void SAL_CALL rtl_cipher_destroy(rtlCipher Cipher) SAL_THROW_EXTERN_C()
+{
+ Cipher_Impl *pImpl = static_cast<Cipher_Impl*>(Cipher);
+ if (pImpl && pImpl->m_delete)
+ pImpl->m_delete(Cipher);
+}
+
+namespace {
+
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
+#define CIPHER_ROUNDS_BF 16
+
+struct CipherKeyBF
+{
+ sal_uInt32 m_S[4][256];
+ sal_uInt32 m_P[CIPHER_ROUNDS_BF + 2];
+};
+#endif
+
+struct CipherContextBF
+{
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+ EVP_CIPHER_CTX * m_context;
+#else
+ CipherKeyBF m_key;
+ union
+ {
+ sal_uInt32 m_long[2];
+ sal_uInt8 m_byte[8];
+ } m_iv;
+ sal_uInt32 m_offset;
+#endif
+};
+
+struct CipherBF_Impl
+{
+ Cipher_Impl m_cipher;
+ CipherContextBF m_context;
+};
+
+}
+
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
+static rtlCipherError BF_init(
+ CipherContextBF *ctx,
+ rtlCipherMode eMode,
+ const sal_uInt8 *pKeyData, sal_Size nKeyLen,
+ const sal_uInt8 *pArgData, sal_Size nArgLen);
+#endif
+
+static rtlCipherError BF_update(
+ CipherContextBF *ctx,
+ rtlCipherMode eMode,
+ rtlCipherDirection eDirection,
+ const sal_uInt8 *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen);
+
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
+static void BF_updateECB(
+ CipherContextBF *ctx,
+ rtlCipherDirection direction,
+ const sal_uInt8 *pData,
+ sal_uInt8 *pBuffer,
+ sal_Size nLength);
+
+static void BF_updateCBC(
+ CipherContextBF *ctx,
+ rtlCipherDirection direction,
+ const sal_uInt8 *pData,
+ sal_uInt8 *pBuffer,
+ sal_Size nLength);
+
+static void BF_updateCFB(
+ CipherContextBF *ctx,
+ rtlCipherDirection direction,
+ const sal_uInt8 *pData,
+ sal_uInt8 *pBuffer);
+
+static void BF_encode(CipherKeyBF *key, sal_uInt32 *xl, sal_uInt32 *xr);
+
+static void BF_decode(CipherKeyBF *key, sal_uInt32 *xl, sal_uInt32 *xr);
+
+static sal_uInt32 BF(CipherKeyBF *key, sal_uInt32 x);
+
+const CipherKeyBF BF_key =
+{
+ /* S */
+ {
+ /* S[0] */
+ {
+ /* 0 */
+ 0xD1310BA6L, 0x98DFB5ACL, 0x2FFD72DBL, 0xD01ADFB7L,
+ 0xB8E1AFEDL, 0x6A267E96L, 0xBA7C9045L, 0xF12C7F99L,
+ 0x24A19947L, 0xB3916CF7L, 0x0801F2E2L, 0x858EFC16L,
+ 0x636920D8L, 0x71574E69L, 0xA458FEA3L, 0xF4933D7EL,
+
+ /* 1 */
+ 0x0D95748FL, 0x728EB658L, 0x718BCD58L, 0x82154AEEL,
+ 0x7B54A41DL, 0xC25A59B5L, 0x9C30D539L, 0x2AF26013L,
+ 0xC5D1B023L, 0x286085F0L, 0xCA417918L, 0xB8DB38EFL,
+ 0x8E79DCB0L, 0x603A180EL, 0x6C9E0E8BL, 0xB01E8A3EL,
+
+ /* 2 */
+ 0xD71577C1L, 0xBD314B27L, 0x78AF2FDAL, 0x55605C60L,
+ 0xE65525F3L, 0xAA55AB94L, 0x57489862L, 0x63E81440L,
+ 0x55CA396AL, 0x2AAB10B6L, 0xB4CC5C34L, 0x1141E8CEL,
+ 0xA15486AFL, 0x7C72E993L, 0xB3EE1411L, 0x636FBC2AL,
+
+ /* 3 */
+ 0x2BA9C55DL, 0x741831F6L, 0xCE5C3E16L, 0x9B87931EL,
+ 0xAFD6BA33L, 0x6C24CF5CL, 0x7A325381L, 0x28958677L,
+ 0x3B8F4898L, 0x6B4BB9AFL, 0xC4BFE81BL, 0x66282193L,
+ 0x61D809CCL, 0xFB21A991L, 0x487CAC60L, 0x5DEC8032L,
+
+ /* 4 */
+ 0xEF845D5DL, 0xE98575B1L, 0xDC262302L, 0xEB651B88L,
+ 0x23893E81L, 0xD396ACC5L, 0x0F6D6FF3L, 0x83F44239L,
+ 0x2E0B4482L, 0xA4842004L, 0x69C8F04AL, 0x9E1F9B5EL,
+ 0x21C66842L, 0xF6E96C9AL, 0x670C9C61L, 0xABD388F0L,
+
+ /* 5 */
+ 0x6A51A0D2L, 0xD8542F68L, 0x960FA728L, 0xAB5133A3L,
+ 0x6EEF0B6CL, 0x137A3BE4L, 0xBA3BF050L, 0x7EFB2A98L,
+ 0xA1F1651DL, 0x39AF0176L, 0x66CA593EL, 0x82430E88L,
+ 0x8CEE8619L, 0x456F9FB4L, 0x7D84A5C3L, 0x3B8B5EBEL,
+
+ /* 6 */
+ 0xE06F75D8L, 0x85C12073L, 0x401A449FL, 0x56C16AA6L,
+ 0x4ED3AA62L, 0x363F7706L, 0x1BFEDF72L, 0x429B023DL,
+ 0x37D0D724L, 0xD00A1248L, 0xDB0FEAD3L, 0x49F1C09BL,
+ 0x075372C9L, 0x80991B7BL, 0x25D479D8L, 0xF6E8DEF7L,
+
+ /* 7 */
+ 0xE3FE501AL, 0xB6794C3BL, 0x976CE0BDL, 0x04C006BAL,
+ 0xC1A94FB6L, 0x409F60C4L, 0x5E5C9EC2L, 0x196A2463L,
+ 0x68FB6FAFL, 0x3E6C53B5L, 0x1339B2EBL, 0x3B52EC6FL,
+ 0x6DFC511FL, 0x9B30952CL, 0xCC814544L, 0xAF5EBD09L,
+
+ /* 8 */
+ 0xBEE3D004L, 0xDE334AFDL, 0x660F2807L, 0x192E4BB3L,
+ 0xC0CBA857L, 0x45C8740FL, 0xD20B5F39L, 0xB9D3FBDBL,
+ 0x5579C0BDL, 0x1A60320AL, 0xD6A100C6L, 0x402C7279L,
+ 0x679F25FEL, 0xFB1FA3CCL, 0x8EA5E9F8L, 0xDB3222F8L,
+
+ /* 9 */
+ 0x3C7516DFL, 0xFD616B15L, 0x2F501EC8L, 0xAD0552ABL,
+ 0x323DB5FAL, 0xFD238760L, 0x53317B48L, 0x3E00DF82L,
+ 0x9E5C57BBL, 0xCA6F8CA0L, 0x1A87562EL, 0xDF1769DBL,
+ 0xD542A8F6L, 0x287EFFC3L, 0xAC6732C6L, 0x8C4F5573L,
+
+ /* A */
+ 0x695B27B0L, 0xBBCA58C8L, 0xE1FFA35DL, 0xB8F011A0L,
+ 0x10FA3D98L, 0xFD2183B8L, 0x4AFCB56CL, 0x2DD1D35BL,
+ 0x9A53E479L, 0xB6F84565L, 0xD28E49BCL, 0x4BFB9790L,
+ 0xE1DDF2DAL, 0xA4CB7E33L, 0x62FB1341L, 0xCEE4C6E8L,
+
+ /* B */
+ 0xEF20CADAL, 0x36774C01L, 0xD07E9EFEL, 0x2BF11FB4L,
+ 0x95DBDA4DL, 0xAE909198L, 0xEAAD8E71L, 0x6B93D5A0L,
+ 0xD08ED1D0L, 0xAFC725E0L, 0x8E3C5B2FL, 0x8E7594B7L,
+ 0x8FF6E2FBL, 0xF2122B64L, 0x8888B812L, 0x900DF01CL,
+
+ /* C */
+ 0x4FAD5EA0L, 0x688FC31CL, 0xD1CFF191L, 0xB3A8C1ADL,
+ 0x2F2F2218L, 0xBE0E1777L, 0xEA752DFEL, 0x8B021FA1L,
+ 0xE5A0CC0FL, 0xB56F74E8L, 0x18ACF3D6L, 0xCE89E299L,
+ 0xB4A84FE0L, 0xFD13E0B7L, 0x7CC43B81L, 0xD2ADA8D9L,
+
+ /* D */
+ 0x165FA266L, 0x80957705L, 0x93CC7314L, 0x211A1477L,
+ 0xE6AD2065L, 0x77B5FA86L, 0xC75442F5L, 0xFB9D35CFL,
+ 0xEBCDAF0CL, 0x7B3E89A0L, 0xD6411BD3L, 0xAE1E7E49L,
+ 0x00250E2DL, 0x2071B35EL, 0x226800BBL, 0x57B8E0AFL,
+
+ /* E */
+ 0x2464369BL, 0xF009B91EL, 0x5563911DL, 0x59DFA6AAL,
+ 0x78C14389L, 0xD95A537FL, 0x207D5BA2L, 0x02E5B9C5L,
+ 0x83260376L, 0x6295CFA9L, 0x11C81968L, 0x4E734A41L,
+ 0xB3472DCAL, 0x7B14A94AL, 0x1B510052L, 0x9A532915L,
+
+ /* F */
+ 0xD60F573FL, 0xBC9BC6E4L, 0x2B60A476L, 0x81E67400L,
+ 0x08BA6FB5L, 0x571BE91FL, 0xF296EC6BL, 0x2A0DD915L,
+ 0xB6636521L, 0xE7B9F9B6L, 0xFF34052EL, 0xC5855664L,
+ 0x53B02D5DL, 0xA99F8FA1L, 0x08BA4799L, 0x6E85076AL
+ },
+
+ /* S[1] */
+ {
+ 0x4B7A70E9L, 0xB5B32944L, 0xDB75092EL, 0xC4192623L,
+ 0xAD6EA6B0L, 0x49A7DF7DL, 0x9CEE60B8L, 0x8FEDB266L,
+ 0xECAA8C71L, 0x699A17FFL, 0x5664526CL, 0xC2B19EE1L,
+ 0x193602A5L, 0x75094C29L, 0xA0591340L, 0xE4183A3EL,
+
+ 0x3F54989AL, 0x5B429D65L, 0x6B8FE4D6L, 0x99F73FD6L,
+ 0xA1D29C07L, 0xEFE830F5L, 0x4D2D38E6L, 0xF0255DC1L,
+ 0x4CDD2086L, 0x8470EB26L, 0x6382E9C6L, 0x021ECC5EL,
+ 0x09686B3FL, 0x3EBAEFC9L, 0x3C971814L, 0x6B6A70A1L,
+
+ 0x687F3584L, 0x52A0E286L, 0xB79C5305L, 0xAA500737L,
+ 0x3E07841CL, 0x7FDEAE5CL, 0x8E7D44ECL, 0x5716F2B8L,
+ 0xB03ADA37L, 0xF0500C0DL, 0xF01C1F04L, 0x0200B3FFL,
+ 0xAE0CF51AL, 0x3CB574B2L, 0x25837A58L, 0xDC0921BDL,
+
+ 0xD19113F9L, 0x7CA92FF6L, 0x94324773L, 0x22F54701L,
+ 0x3AE5E581L, 0x37C2DADCL, 0xC8B57634L, 0x9AF3DDA7L,
+ 0xA9446146L, 0x0FD0030EL, 0xECC8C73EL, 0xA4751E41L,
+ 0xE238CD99L, 0x3BEA0E2FL, 0x3280BBA1L, 0x183EB331L,
+
+ 0x4E548B38L, 0x4F6DB908L, 0x6F420D03L, 0xF60A04BFL,
+ 0x2CB81290L, 0x24977C79L, 0x5679B072L, 0xBCAF89AFL,
+ 0xDE9A771FL, 0xD9930810L, 0xB38BAE12L, 0xDCCF3F2EL,
+ 0x5512721FL, 0x2E6B7124L, 0x501ADDE6L, 0x9F84CD87L,
+
+ 0x7A584718L, 0x7408DA17L, 0xBC9F9ABCL, 0xE94B7D8CL,
+ 0xEC7AEC3AL, 0xDB851DFAL, 0x63094366L, 0xC464C3D2L,
+ 0xEF1C1847L, 0x3215D908L, 0xDD433B37L, 0x24C2BA16L,
+ 0x12A14D43L, 0x2A65C451L, 0x50940002L, 0x133AE4DDL,
+
+ 0x71DFF89EL, 0x10314E55L, 0x81AC77D6L, 0x5F11199BL,
+ 0x043556F1L, 0xD7A3C76BL, 0x3C11183BL, 0x5924A509L,
+ 0xF28FE6EDL, 0x97F1FBFAL, 0x9EBABF2CL, 0x1E153C6EL,
+ 0x86E34570L, 0xEAE96FB1L, 0x860E5E0AL, 0x5A3E2AB3L,
+
+ 0x771FE71CL, 0x4E3D06FAL, 0x2965DCB9L, 0x99E71D0FL,
+ 0x803E89D6L, 0x5266C825L, 0x2E4CC978L, 0x9C10B36AL,
+ 0xC6150EBAL, 0x94E2EA78L, 0xA5FC3C53L, 0x1E0A2DF4L,
+ 0xF2F74EA7L, 0x361D2B3DL, 0x1939260FL, 0x19C27960L,
+
+ 0x5223A708L, 0xF71312B6L, 0xEBADFE6EL, 0xEAC31F66L,
+ 0xE3BC4595L, 0xA67BC883L, 0xB17F37D1L, 0x018CFF28L,
+ 0xC332DDEFL, 0xBE6C5AA5L, 0x65582185L, 0x68AB9802L,
+ 0xEECEA50FL, 0xDB2F953BL, 0x2AEF7DADL, 0x5B6E2F84L,
+
+ 0x1521B628L, 0x29076170L, 0xECDD4775L, 0x619F1510L,
+ 0x13CCA830L, 0xEB61BD96L, 0x0334FE1EL, 0xAA0363CFL,
+ 0xB5735C90L, 0x4C70A239L, 0xD59E9E0BL, 0xCBAADE14L,
+ 0xEECC86BCL, 0x60622CA7L, 0x9CAB5CABL, 0xB2F3846EL,
+
+ 0x648B1EAFL, 0x19BDF0CAL, 0xA02369B9L, 0x655ABB50L,
+ 0x40685A32L, 0x3C2AB4B3L, 0x319EE9D5L, 0xC021B8F7L,
+ 0x9B540B19L, 0x875FA099L, 0x95F7997EL, 0x623D7DA8L,
+ 0xF837889AL, 0x97E32D77L, 0x11ED935FL, 0x16681281L,
+
+ 0x0E358829L, 0xC7E61FD6L, 0x96DEDFA1L, 0x7858BA99L,
+ 0x57F584A5L, 0x1B227263L, 0x9B83C3FFL, 0x1AC24696L,
+ 0xCDB30AEBL, 0x532E3054L, 0x8FD948E4L, 0x6DBC3128L,
+ 0x58EBF2EFL, 0x34C6FFEAL, 0xFE28ED61L, 0xEE7C3C73L,
+
+ 0x5D4A14D9L, 0xE864B7E3L, 0x42105D14L, 0x203E13E0L,
+ 0x45EEE2B6L, 0xA3AAABEAL, 0xDB6C4F15L, 0xFACB4FD0L,
+ 0xC742F442L, 0xEF6ABBB5L, 0x654F3B1DL, 0x41CD2105L,
+ 0xD81E799EL, 0x86854DC7L, 0xE44B476AL, 0x3D816250L,
+
+ 0xCF62A1F2L, 0x5B8D2646L, 0xFC8883A0L, 0xC1C7B6A3L,
+ 0x7F1524C3L, 0x69CB7492L, 0x47848A0BL, 0x5692B285L,
+ 0x095BBF00L, 0xAD19489DL, 0x1462B174L, 0x23820E00L,
+ 0x58428D2AL, 0x0C55F5EAL, 0x1DADF43EL, 0x233F7061L,
+
+ 0x3372F092L, 0x8D937E41L, 0xD65FECF1L, 0x6C223BDBL,
+ 0x7CDE3759L, 0xCBEE7460L, 0x4085F2A7L, 0xCE77326EL,
+ 0xA6078084L, 0x19F8509EL, 0xE8EFD855L, 0x61D99735L,
+ 0xA969A7AAL, 0xC50C06C2L, 0x5A04ABFCL, 0x800BCADCL,
+
+ 0x9E447A2EL, 0xC3453484L, 0xFDD56705L, 0x0E1E9EC9L,
+ 0xDB73DBD3L, 0x105588CDL, 0x675FDA79L, 0xE3674340L,
+ 0xC5C43465L, 0x713E38D8L, 0x3D28F89EL, 0xF16DFF20L,
+ 0x153E21E7L, 0x8FB03D4AL, 0xE6E39F2BL, 0xDB83ADF7L
+ },
+
+ /* S[2] */
+ {
+ 0xE93D5A68L, 0x948140F7L, 0xF64C261CL, 0x94692934L,
+ 0x411520F7L, 0x7602D4F7L, 0xBCF46B2EL, 0xD4A20068L,
+ 0xD4082471L, 0x3320F46AL, 0x43B7D4B7L, 0x500061AFL,
+ 0x1E39F62EL, 0x97244546L, 0x14214F74L, 0xBF8B8840L,
+
+ 0x4D95FC1DL, 0x96B591AFL, 0x70F4DDD3L, 0x66A02F45L,
+ 0xBFBC09ECL, 0x03BD9785L, 0x7FAC6DD0L, 0x31CB8504L,
+ 0x96EB27B3L, 0x55FD3941L, 0xDA2547E6L, 0xABCA0A9AL,
+ 0x28507825L, 0x530429F4L, 0x0A2C86DAL, 0xE9B66DFBL,
+
+ 0x68DC1462L, 0xD7486900L, 0x680EC0A4L, 0x27A18DEEL,
+ 0x4F3FFEA2L, 0xE887AD8CL, 0xB58CE006L, 0x7AF4D6B6L,
+ 0xAACE1E7CL, 0xD3375FECL, 0xCE78A399L, 0x406B2A42L,
+ 0x20FE9E35L, 0xD9F385B9L, 0xEE39D7ABL, 0x3B124E8BL,
+
+ 0x1DC9FAF7L, 0x4B6D1856L, 0x26A36631L, 0xEAE397B2L,
+ 0x3A6EFA74L, 0xDD5B4332L, 0x6841E7F7L, 0xCA7820FBL,
+ 0xFB0AF54EL, 0xD8FEB397L, 0x454056ACL, 0xBA489527L,
+ 0x55533A3AL, 0x20838D87L, 0xFE6BA9B7L, 0xD096954BL,
+
+ 0x55A867BCL, 0xA1159A58L, 0xCCA92963L, 0x99E1DB33L,
+ 0xA62A4A56L, 0x3F3125F9L, 0x5EF47E1CL, 0x9029317CL,
+ 0xFDF8E802L, 0x04272F70L, 0x80BB155CL, 0x05282CE3L,
+ 0x95C11548L, 0xE4C66D22L, 0x48C1133FL, 0xC70F86DCL,
+
+ 0x07F9C9EEL, 0x41041F0FL, 0x404779A4L, 0x5D886E17L,
+ 0x325F51EBL, 0xD59BC0D1L, 0xF2BCC18FL, 0x41113564L,
+ 0x257B7834L, 0x602A9C60L, 0xDFF8E8A3L, 0x1F636C1BL,
+ 0x0E12B4C2L, 0x02E1329EL, 0xAF664FD1L, 0xCAD18115L,
+
+ 0x6B2395E0L, 0x333E92E1L, 0x3B240B62L, 0xEEBEB922L,
+ 0x85B2A20EL, 0xE6BA0D99L, 0xDE720C8CL, 0x2DA2F728L,
+ 0xD0127845L, 0x95B794FDL, 0x647D0862L, 0xE7CCF5F0L,
+ 0x5449A36FL, 0x877D48FAL, 0xC39DFD27L, 0xF33E8D1EL,
+
+ 0x0A476341L, 0x992EFF74L, 0x3A6F6EABL, 0xF4F8FD37L,
+ 0xA812DC60L, 0xA1EBDDF8L, 0x991BE14CL, 0xDB6E6B0DL,
+ 0xC67B5510L, 0x6D672C37L, 0x2765D43BL, 0xDCD0E804L,
+ 0xF1290DC7L, 0xCC00FFA3L, 0xB5390F92L, 0x690FED0BL,
+
+ 0x667B9FFBL, 0xCEDB7D9CL, 0xA091CF0BL, 0xD9155EA3L,
+ 0xBB132F88L, 0x515BAD24L, 0x7B9479BFL, 0x763BD6EBL,
+ 0x37392EB3L, 0xCC115979L, 0x8026E297L, 0xF42E312DL,
+ 0x6842ADA7L, 0xC66A2B3BL, 0x12754CCCL, 0x782EF11CL,
+
+ 0x6A124237L, 0xB79251E7L, 0x06A1BBE6L, 0x4BFB6350L,
+ 0x1A6B1018L, 0x11CAEDFAL, 0x3D25BDD8L, 0xE2E1C3C9L,
+ 0x44421659L, 0x0A121386L, 0xD90CEC6EL, 0xD5ABEA2AL,
+ 0x64AF674EL, 0xDA86A85FL, 0xBEBFE988L, 0x64E4C3FEL,
+
+ 0x9DBC8057L, 0xF0F7C086L, 0x60787BF8L, 0x6003604DL,
+ 0xD1FD8346L, 0xF6381FB0L, 0x7745AE04L, 0xD736FCCCL,
+ 0x83426B33L, 0xF01EAB71L, 0xB0804187L, 0x3C005E5FL,
+ 0x77A057BEL, 0xBDE8AE24L, 0x55464299L, 0xBF582E61L,
+
+ 0x4E58F48FL, 0xF2DDFDA2L, 0xF474EF38L, 0x8789BDC2L,
+ 0x5366F9C3L, 0xC8B38E74L, 0xB475F255L, 0x46FCD9B9L,
+ 0x7AEB2661L, 0x8B1DDF84L, 0x846A0E79L, 0x915F95E2L,
+ 0x466E598EL, 0x20B45770L, 0x8CD55591L, 0xC902DE4CL,
+
+ 0xB90BACE1L, 0xBB8205D0L, 0x11A86248L, 0x7574A99EL,
+ 0xB77F19B6L, 0xE0A9DC09L, 0x662D09A1L, 0xC4324633L,
+ 0xE85A1F02L, 0x09F0BE8CL, 0x4A99A025L, 0x1D6EFE10L,
+ 0x1AB93D1DL, 0x0BA5A4DFL, 0xA186F20FL, 0x2868F169L,
+
+ 0xDCB7DA83L, 0x573906FEL, 0xA1E2CE9BL, 0x4FCD7F52L,
+ 0x50115E01L, 0xA70683FAL, 0xA002B5C4L, 0x0DE6D027L,
+ 0x9AF88C27L, 0x773F8641L, 0xC3604C06L, 0x61A806B5L,
+ 0xF0177A28L, 0xC0F586E0L, 0x006058AAL, 0x30DC7D62L,
+
+ 0x11E69ED7L, 0x2338EA63L, 0x53C2DD94L, 0xC2C21634L,
+ 0xBBCBEE56L, 0x90BCB6DEL, 0xEBFC7DA1L, 0xCE591D76L,
+ 0x6F05E409L, 0x4B7C0188L, 0x39720A3DL, 0x7C927C24L,
+ 0x86E3725FL, 0x724D9DB9L, 0x1AC15BB4L, 0xD39EB8FCL,
+
+ 0xED545578L, 0x08FCA5B5L, 0xD83D7CD3L, 0x4DAD0FC4L,
+ 0x1E50EF5EL, 0xB161E6F8L, 0xA28514D9L, 0x6C51133CL,
+ 0x6FD5C7E7L, 0x56E14EC4L, 0x362ABFCEL, 0xDDC6C837L,
+ 0xD79A3234L, 0x92638212L, 0x670EFA8EL, 0x406000E0L
+ },
+
+ /* S[3] */
+ {
+ 0x3A39CE37L, 0xD3FAF5CFL, 0xABC27737L, 0x5AC52D1BL,
+ 0x5CB0679EL, 0x4FA33742L, 0xD3822740L, 0x99BC9BBEL,
+ 0xD5118E9DL, 0xBF0F7315L, 0xD62D1C7EL, 0xC700C47BL,
+ 0xB78C1B6BL, 0x21A19045L, 0xB26EB1BEL, 0x6A366EB4L,
+
+ 0x5748AB2FL, 0xBC946E79L, 0xC6A376D2L, 0x6549C2C8L,
+ 0x530FF8EEL, 0x468DDE7DL, 0xD5730A1DL, 0x4CD04DC6L,
+ 0x2939BBDBL, 0xA9BA4650L, 0xAC9526E8L, 0xBE5EE304L,
+ 0xA1FAD5F0L, 0x6A2D519AL, 0x63EF8CE2L, 0x9A86EE22L,
+
+ 0xC089C2B8L, 0x43242EF6L, 0xA51E03AAL, 0x9CF2D0A4L,
+ 0x83C061BAL, 0x9BE96A4DL, 0x8FE51550L, 0xBA645BD6L,
+ 0x2826A2F9L, 0xA73A3AE1L, 0x4BA99586L, 0xEF5562E9L,
+ 0xC72FEFD3L, 0xF752F7DAL, 0x3F046F69L, 0x77FA0A59L,
+
+ 0x80E4A915L, 0x87B08601L, 0x9B09E6ADL, 0x3B3EE593L,
+ 0xE990FD5AL, 0x9E34D797L, 0x2CF0B7D9L, 0x022B8B51L,
+ 0x96D5AC3AL, 0x017DA67DL, 0xD1CF3ED6L, 0x7C7D2D28L,
+ 0x1F9F25CFL, 0xADF2B89BL, 0x5AD6B472L, 0x5A88F54CL,
+
+ 0xE029AC71L, 0xE019A5E6L, 0x47B0ACFDL, 0xED93FA9BL,
+ 0xE8D3C48DL, 0x283B57CCL, 0xF8D56629L, 0x79132E28L,
+ 0x785F0191L, 0xED756055L, 0xF7960E44L, 0xE3D35E8CL,
+ 0x15056DD4L, 0x88F46DBAL, 0x03A16125L, 0x0564F0BDL,
+
+ 0xC3EB9E15L, 0x3C9057A2L, 0x97271AECL, 0xA93A072AL,
+ 0x1B3F6D9BL, 0x1E6321F5L, 0xF59C66FBL, 0x26DCF319L,
+ 0x7533D928L, 0xB155FDF5L, 0x03563482L, 0x8ABA3CBBL,
+ 0x28517711L, 0xC20AD9F8L, 0xABCC5167L, 0xCCAD925FL,
+
+ 0x4DE81751L, 0x3830DC8EL, 0x379D5862L, 0x9320F991L,
+ 0xEA7A90C2L, 0xFB3E7BCEL, 0x5121CE64L, 0x774FBE32L,
+ 0xA8B6E37EL, 0xC3293D46L, 0x48DE5369L, 0x6413E680L,
+ 0xA2AE0810L, 0xDD6DB224L, 0x69852DFDL, 0x09072166L,
+
+ 0xB39A460AL, 0x6445C0DDL, 0x586CDECFL, 0x1C20C8AEL,
+ 0x5BBEF7DDL, 0x1B588D40L, 0xCCD2017FL, 0x6BB4E3BBL,
+ 0xDDA26A7EL, 0x3A59FF45L, 0x3E350A44L, 0xBCB4CDD5L,
+ 0x72EACEA8L, 0xFA6484BBL, 0x8D6612AEL, 0xBF3C6F47L,
+
+ 0xD29BE463L, 0x542F5D9EL, 0xAEC2771BL, 0xF64E6370L,
+ 0x740E0D8DL, 0xE75B1357L, 0xF8721671L, 0xAF537D5DL,
+ 0x4040CB08L, 0x4EB4E2CCL, 0x34D2466AL, 0x0115AF84L,
+ 0xE1B00428L, 0x95983A1DL, 0x06B89FB4L, 0xCE6EA048L,
+
+ 0x6F3F3B82L, 0x3520AB82L, 0x011A1D4BL, 0x277227F8L,
+ 0x611560B1L, 0xE7933FDCL, 0xBB3A792BL, 0x344525BDL,
+ 0xA08839E1L, 0x51CE794BL, 0x2F32C9B7L, 0xA01FBAC9L,
+ 0xE01CC87EL, 0xBCC7D1F6L, 0xCF0111C3L, 0xA1E8AAC7L,
+
+ 0x1A908749L, 0xD44FBD9AL, 0xD0DADECBL, 0xD50ADA38L,
+ 0x0339C32AL, 0xC6913667L, 0x8DF9317CL, 0xE0B12B4FL,
+ 0xF79E59B7L, 0x43F5BB3AL, 0xF2D519FFL, 0x27D9459CL,
+ 0xBF97222CL, 0x15E6FC2AL, 0x0F91FC71L, 0x9B941525L,
+
+ 0xFAE59361L, 0xCEB69CEBL, 0xC2A86459L, 0x12BAA8D1L,
+ 0xB6C1075EL, 0xE3056A0CL, 0x10D25065L, 0xCB03A442L,
+ 0xE0EC6E0EL, 0x1698DB3BL, 0x4C98A0BEL, 0x3278E964L,
+ 0x9F1F9532L, 0xE0D392DFL, 0xD3A0342BL, 0x8971F21EL,
+
+ 0x1B0A7441L, 0x4BA3348CL, 0xC5BE7120L, 0xC37632D8L,
+ 0xDF359F8DL, 0x9B992F2EL, 0xE60B6F47L, 0x0FE3F11DL,
+ 0xE54CDA54L, 0x1EDAD891L, 0xCE6279CFL, 0xCD3E7E6FL,
+ 0x1618B166L, 0xFD2C1D05L, 0x848FD2C5L, 0xF6FB2299L,
+
+ 0xF523F357L, 0xA6327623L, 0x93A83531L, 0x56CCCD02L,
+ 0xACF08162L, 0x5A75EBB5L, 0x6E163697L, 0x88D273CCL,
+ 0xDE966292L, 0x81B949D0L, 0x4C50901BL, 0x71C65614L,
+ 0xE6C6C7BDL, 0x327A140AL, 0x45E1D006L, 0xC3F27B9AL,
+
+ 0xC9AA53FDL, 0x62A80F00L, 0xBB25BFE2L, 0x35BDD2F6L,
+ 0x71126905L, 0xB2040222L, 0xB6CBCF7CL, 0xCD769C2BL,
+ 0x53113EC0L, 0x1640E3D3L, 0x38ABBD60L, 0x2547ADF0L,
+ 0xBA38209CL, 0xF746CE76L, 0x77AFA1C5L, 0x20756060L,
+
+ 0x85CBFE4EL, 0x8AE88DD8L, 0x7AAAF9B0L, 0x4CF9AA7EL,
+ 0x1948C25CL, 0x02FB8A8CL, 0x01C36AE4L, 0xD6EBE1F9L,
+ 0x90D4F869L, 0xA65CDEA0L, 0x3F09252DL, 0xC208E69FL,
+ 0xB74E6132L, 0xCE77E25BL, 0x578FDFE3L, 0x3AC372E6L
+ }
+ },
+
+ /* P */
+ {
+ 0x243F6A88L, 0x85A308D3L, 0x13198A2EL, 0x03707344L,
+ 0xA4093822L, 0x299F31D0L, 0x082EFA98L, 0xEC4E6C89L,
+ 0x452821E6L, 0x38D01377L, 0xBE5466CFL, 0x34E90C6CL,
+ 0xC0AC29B7L, 0xC97C50DDL, 0x3F84D5B5L, 0xB5470917L,
+ 0x9216D5D9L, 0x8979FB1BL
+ }
+};
+#endif
+
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
+static rtlCipherError BF_init(
+ CipherContextBF *ctx,
+ rtlCipherMode eMode,
+ const sal_uInt8 *pKeyData, sal_Size nKeyLen,
+ const sal_uInt8 *pArgData, sal_Size nArgLen)
+{
+ CipherKeyBF *key;
+ sal_uInt32 D, DL, DR;
+ sal_uInt16 i, j, k;
+
+ key = &(ctx->m_key);
+
+ memcpy(key, &BF_key, sizeof (CipherKeyBF));
+ memset(&(ctx->m_iv), 0, sizeof(ctx->m_iv));
+ ctx->m_offset = 0;
+
+ for (i = 0, k = 0; i < CIPHER_ROUNDS_BF + 2; ++i)
+ {
+ D = 0;
+ for (j = 0; j < 4; ++j)
+ {
+ D = ((D << 8) | pKeyData[k]);
+ k++;
+ if (k >= nKeyLen)
+ k = 0;
+ }
+ key->m_P[i] ^= D;
+ }
+
+ rtl_secureZeroMemory(&DL, sizeof(DL));
+ rtl_secureZeroMemory(&DR, sizeof(DR));
+
+ for (i = 0; i < CIPHER_ROUNDS_BF + 2; i += 2)
+ {
+ BF_encode(key, &DL, &DR);
+ key->m_P[i ] = DL;
+ key->m_P[i + 1] = DR;
+ }
+
+ for (i = 0; i < 4; ++i)
+ {
+ for (k = 0; k < 256; k += 2)
+ {
+ BF_encode(key, &DL, &DR);
+ key->m_S[i][k ] = DL;
+ key->m_S[i][k + 1] = DR;
+ }
+ }
+
+ if (pArgData && nArgLen)
+ {
+ nArgLen = std::min<sal_Size>(nArgLen, 8);
+ if (eMode == rtl_Cipher_ModeStream)
+ {
+ memcpy(ctx->m_iv.m_byte, pArgData, nArgLen);
+ }
+ else
+ {
+ RTL_CIPHER_NTOHL64 (pArgData, DL, DR, nArgLen);
+ ctx->m_iv.m_long[0] = DL;
+ ctx->m_iv.m_long[1] = DR;
+ }
+ }
+
+ return rtl_Cipher_E_None;
+}
+#endif
+
+static rtlCipherError BF_update(
+ CipherContextBF *ctx,
+ rtlCipherMode eMode,
+ rtlCipherDirection eDirection,
+ const sal_uInt8 *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen)
+{
+ /* Check arguments. */
+ if (!pData || !pBuffer)
+ return rtl_Cipher_E_Argument;
+
+ if ((nDatLen <= 0) || (nDatLen > nBufLen))
+ return rtl_Cipher_E_BufferSize;
+
+ /* Update. */
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+ assert(eMode == rtl_Cipher_ModeStream);
+ (void) eMode;
+ (void) eDirection;
+ while (nDatLen > o3tl::make_unsigned(std::numeric_limits<int>::max())) {
+ int outl;
+ if (EVP_CipherUpdate(ctx->m_context, pBuffer, &outl, pData, std::numeric_limits<int>::max())
+ == 0)
+ {
+ return rtl_Cipher_E_Unknown;
+ }
+ assert(outl == std::numeric_limits<int>::max());
+ pData += std::numeric_limits<int>::max();
+ nDatLen -= std::numeric_limits<int>::max();
+ pBuffer += std::numeric_limits<int>::max();
+ }
+ int outl;
+ if (EVP_CipherUpdate(ctx->m_context, pBuffer, &outl, pData, static_cast<int>(nDatLen)) == 0)
+ {
+ return rtl_Cipher_E_Unknown;
+ }
+ assert(outl == static_cast<int>(nDatLen));
+ // A final call to EVP_CipherFinal_ex is intentionally missing; it wouldn't fit the rtl/cipher.h
+ // interface, and is hopefully not needed, as each individual Blowfish CFB update step doesn't
+ // hold back any data that would need to be finally flushed.
+#else
+ if (eMode == rtl_Cipher_ModeECB)
+ {
+ /* Block mode. */
+ while (nDatLen > 8)
+ {
+ BF_updateECB(ctx, eDirection, pData, pBuffer, 8);
+ nDatLen -= 8;
+ pData += 8;
+ pBuffer += 8;
+ }
+ BF_updateECB(ctx, eDirection, pData, pBuffer, nDatLen);
+ }
+ else if (eMode == rtl_Cipher_ModeCBC)
+ {
+ /* Block mode. */
+ while (nDatLen > 8)
+ {
+ BF_updateCBC (ctx, eDirection, pData, pBuffer, 8);
+ nDatLen -= 8;
+ pData += 8;
+ pBuffer += 8;
+ }
+ BF_updateCBC (ctx, eDirection, pData, pBuffer, nDatLen);
+ }
+ else
+ {
+ /* Stream mode. */
+ while (nDatLen > 0)
+ {
+ BF_updateCFB (ctx, eDirection, pData, pBuffer);
+ nDatLen -= 1;
+ pData += 1;
+ pBuffer += 1;
+ }
+ }
+#endif
+ return rtl_Cipher_E_None;
+}
+
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
+static void BF_updateECB(
+ CipherContextBF *ctx,
+ rtlCipherDirection direction,
+ const sal_uInt8 *pData,
+ sal_uInt8 *pBuffer,
+ sal_Size nLength)
+{
+ CipherKeyBF *key;
+ sal_uInt32 DL, DR;
+
+ key = &(ctx->m_key);
+ if (direction == rtl_Cipher_DirectionEncode)
+ {
+ RTL_CIPHER_NTOHL64(pData, DL, DR, nLength);
+
+ BF_encode(key, &DL, &DR);
+
+ RTL_CIPHER_HTONL(DL, pBuffer);
+ RTL_CIPHER_HTONL(DR, pBuffer);
+ }
+ else
+ {
+ RTL_CIPHER_NTOHL(pData, DL);
+ RTL_CIPHER_NTOHL(pData, DR);
+
+ BF_decode(key, &DL, &DR);
+
+ RTL_CIPHER_HTONL64(DL, DR, pBuffer, nLength);
+ }
+ rtl_secureZeroMemory(&DL, sizeof(DL));
+ rtl_secureZeroMemory(&DR, sizeof(DR));
+}
+
+static void BF_updateCBC(
+ CipherContextBF *ctx,
+ rtlCipherDirection direction,
+ const sal_uInt8 *pData,
+ sal_uInt8 *pBuffer,
+ sal_Size nLength)
+{
+ CipherKeyBF *key;
+ sal_uInt32 DL, DR;
+
+ key = &(ctx->m_key);
+ if (direction == rtl_Cipher_DirectionEncode)
+ {
+ RTL_CIPHER_NTOHL64(pData, DL, DR, nLength);
+
+ DL ^= ctx->m_iv.m_long[0];
+ DR ^= ctx->m_iv.m_long[1];
+
+ BF_encode(key, &DL, &DR);
+
+ ctx->m_iv.m_long[0] = DL;
+ ctx->m_iv.m_long[1] = DR;
+
+ RTL_CIPHER_HTONL(DL, pBuffer);
+ RTL_CIPHER_HTONL(DR, pBuffer);
+ }
+ else
+ {
+ sal_uInt32 IVL, IVR;
+
+ RTL_CIPHER_NTOHL(pData, DL);
+ RTL_CIPHER_NTOHL(pData, DR);
+
+ IVL = DL;
+ IVR = DR;
+
+ BF_decode(key, &DL, &DR);
+
+ DL ^= ctx->m_iv.m_long[0];
+ DR ^= ctx->m_iv.m_long[1];
+
+ ctx->m_iv.m_long[0] = IVL;
+ ctx->m_iv.m_long[1] = IVR;
+
+ RTL_CIPHER_HTONL64(DL, DR, pBuffer, nLength);
+ }
+ rtl_secureZeroMemory(&DL, sizeof(DL));
+ rtl_secureZeroMemory(&DR, sizeof(DR));
+}
+
+static void BF_updateCFB(
+ CipherContextBF *ctx,
+ rtlCipherDirection direction,
+ const sal_uInt8 *pData,
+ sal_uInt8 *pBuffer)
+{
+ sal_uInt8 *iv;
+ sal_uInt32 k;
+
+ iv = ctx->m_iv.m_byte;
+ k = ctx->m_offset;
+
+ if (k == 0)
+ {
+ sal_uInt32 IVL, IVR;
+
+ RTL_CIPHER_NTOHL64(iv, IVL, IVR, 8);
+ BF_encode(&(ctx->m_key), &IVL, &IVR);
+ RTL_CIPHER_HTONL64(IVL, IVR, iv, 8);
+
+ rtl_secureZeroMemory(&IVL, sizeof(IVL));
+ rtl_secureZeroMemory(&IVR, sizeof(IVR));
+ }
+
+ if (direction == rtl_Cipher_DirectionEncode)
+ {
+ iv[k] ^= *pData;
+ *pBuffer = iv[k];
+ }
+ else
+ {
+ sal_uInt8 c = iv[k];
+ iv[k] = *pData;
+ *pBuffer = *pData ^ c;
+ }
+
+ ctx->m_offset = ((k + 1) & 0x07);
+ iv = nullptr;
+}
+
+static void BF_encode(
+ CipherKeyBF *key, sal_uInt32 *xl, sal_uInt32 *xr)
+{
+ sal_uInt32 t, XL, XR;
+ sal_uInt16 i;
+
+ XL = *xl;
+ XR = *xr;
+
+ for (i = 0; i < CIPHER_ROUNDS_BF; ++i)
+ {
+ XL ^= key->m_P[i];
+ XR ^= BF (key, XL);
+
+ t = XL;
+ XL = XR;
+ XR = t;
+ }
+
+ t = XL;
+ XL = XR;
+ XR = t;
+
+ XR ^= key->m_P[CIPHER_ROUNDS_BF ];
+ XL ^= key->m_P[CIPHER_ROUNDS_BF + 1];
+
+ *xl = XL;
+ *xr = XR;
+
+}
+
+static void BF_decode(
+ CipherKeyBF *key, sal_uInt32 *xl, sal_uInt32 *xr)
+{
+ sal_uInt32 t, XL, XR;
+ sal_uInt16 i;
+
+ XL = *xl;
+ XR = *xr;
+
+ for (i = CIPHER_ROUNDS_BF + 1; i > 1; --i)
+ {
+ XL ^= key->m_P[i];
+ XR ^= BF (key, XL);
+
+ t = XL;
+ XL = XR;
+ XR = t;
+ }
+
+ t = XL;
+ XL = XR;
+ XR = t;
+
+ XR ^= key->m_P[1];
+ XL ^= key->m_P[0];
+
+ *xl = XL;
+ *xr = XR;
+
+}
+
+static sal_uInt32 BF(CipherKeyBF *key, sal_uInt32 x)
+{
+ sal_uInt16 a, b, c, d;
+ sal_uInt32 y;
+
+ d = static_cast<sal_uInt16>(x & 0x00ff);
+ x >>= 8;
+ c = static_cast<sal_uInt16>(x & 0x00ff);
+ x >>= 8;
+ b = static_cast<sal_uInt16>(x & 0x00ff);
+ x >>= 8;
+ a = static_cast<sal_uInt16>(x & 0x00ff);
+
+ y = key->m_S[0][a];
+ y += key->m_S[1][b];
+ y ^= key->m_S[2][c];
+ y += key->m_S[3][d];
+
+ return y;
+}
+#endif
+
+/**
+ rtl_cipherBF (Blowfish) implementation.
+
+ Reference: Bruce Schneier: Applied Cryptography, 2nd edition, ch. 14.3
+*/
+rtlCipher SAL_CALL rtl_cipher_createBF(rtlCipherMode Mode) SAL_THROW_EXTERN_C()
+{
+ CipherBF_Impl *pImpl = nullptr;
+
+ if (Mode == rtl_Cipher_ModeInvalid)
+ return nullptr;
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+ if (Mode != rtl_Cipher_ModeStream) {
+ // Cannot easily support ModeECB and ModeCBC, and they aren't used in the LO code at least:
+ return nullptr;
+ }
+#endif
+
+ pImpl = static_cast<CipherBF_Impl*>(rtl_allocateZeroMemory(sizeof (CipherBF_Impl)));
+ if (pImpl)
+ {
+ pImpl->m_cipher.m_algorithm = rtl_Cipher_AlgorithmBF;
+ pImpl->m_cipher.m_direction = rtl_Cipher_DirectionInvalid;
+ pImpl->m_cipher.m_mode = Mode;
+
+ pImpl->m_cipher.m_init = rtl_cipher_initBF;
+ pImpl->m_cipher.m_encode = rtl_cipher_encodeBF;
+ pImpl->m_cipher.m_decode = rtl_cipher_decodeBF;
+ pImpl->m_cipher.m_delete = rtl_cipher_destroyBF;
+ }
+ return static_cast<rtlCipher>(pImpl);
+}
+
+rtlCipherError SAL_CALL rtl_cipher_initBF(
+ rtlCipher Cipher,
+ rtlCipherDirection Direction,
+ const sal_uInt8 *pKeyData, sal_Size nKeyLen,
+ const sal_uInt8 *pArgData, sal_Size nArgLen) SAL_THROW_EXTERN_C()
+{
+ CipherBF_Impl *pImpl = static_cast<CipherBF_Impl*>(Cipher);
+
+ if (!pImpl || !pKeyData)
+ return rtl_Cipher_E_Argument;
+
+ if (pImpl->m_cipher.m_algorithm != rtl_Cipher_AlgorithmBF)
+ return rtl_Cipher_E_Algorithm;
+
+ if (Direction != rtl_Cipher_DirectionInvalid)
+ pImpl->m_cipher.m_direction = Direction;
+ else
+ return rtl_Cipher_E_Direction;
+
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+ if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionBoth) {
+ // Cannot easily support DirectionBoth, and it isn't used in the LO code at least:
+ return rtl_Cipher_E_Direction;
+ }
+ if (nKeyLen > o3tl::make_unsigned(std::numeric_limits<int>::max())) {
+ return rtl_Cipher_E_BufferSize;
+ }
+ if (pImpl->m_context.m_context != nullptr) {
+ EVP_CIPHER_CTX_free(pImpl->m_context.m_context);
+ }
+ pImpl->m_context.m_context = EVP_CIPHER_CTX_new();
+ if (pImpl->m_context.m_context == nullptr) {
+ return rtl_Cipher_E_Memory;
+ }
+ unsigned char iv[8];
+ auto const n = std::min(nArgLen, sal_Size(8));
+ std::memcpy(iv, pArgData, n);
+ std::memset(iv + n, 0, 8 - n);
+ if (EVP_CipherInit_ex(
+ pImpl->m_context.m_context, EVP_bf_cfb(), nullptr, nullptr, iv,
+ pImpl->m_cipher.m_direction == rtl_Cipher_DirectionDecode ? 0 : 1)
+ == 0)
+ {
+ return rtl_Cipher_E_Unknown;
+ }
+ if (EVP_CIPHER_CTX_set_key_length(pImpl->m_context.m_context, static_cast<int>(nKeyLen)) == 0) {
+ return rtl_Cipher_E_Unknown;
+ }
+ if (EVP_CipherInit_ex(pImpl->m_context.m_context, nullptr, nullptr, pKeyData, nullptr, -1) == 0)
+ {
+ return rtl_Cipher_E_Unknown;
+ }
+ return rtl_Cipher_E_None;
+#else
+ return BF_init(
+ &(pImpl->m_context), pImpl->m_cipher.m_mode,
+ pKeyData, nKeyLen, pArgData, nArgLen);
+#endif
+}
+
+rtlCipherError SAL_CALL rtl_cipher_encodeBF(
+ rtlCipher Cipher,
+ const void *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen) SAL_THROW_EXTERN_C()
+{
+ CipherBF_Impl *pImpl = static_cast<CipherBF_Impl*>(Cipher);
+ if (!pImpl)
+ return rtl_Cipher_E_Argument;
+
+ if (pImpl->m_cipher.m_algorithm != rtl_Cipher_AlgorithmBF)
+ return rtl_Cipher_E_Algorithm;
+
+ if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionInvalid)
+ return rtl_Cipher_E_Direction;
+
+ if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionDecode)
+ return rtl_Cipher_E_Direction;
+
+ return BF_update(
+ &(pImpl->m_context), pImpl->m_cipher.m_mode,
+ rtl_Cipher_DirectionEncode,
+ static_cast<const sal_uInt8*>(pData), nDatLen, pBuffer, nBufLen);
+}
+
+rtlCipherError SAL_CALL rtl_cipher_decodeBF(
+ rtlCipher Cipher,
+ const void *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen) SAL_THROW_EXTERN_C()
+{
+ CipherBF_Impl *pImpl = static_cast<CipherBF_Impl*>(Cipher);
+ if (!pImpl)
+ return rtl_Cipher_E_Argument;
+
+ if (pImpl->m_cipher.m_algorithm != rtl_Cipher_AlgorithmBF)
+ return rtl_Cipher_E_Algorithm;
+
+ if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionInvalid)
+ return rtl_Cipher_E_Direction;
+
+ if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionEncode)
+ return rtl_Cipher_E_Direction;
+
+ return BF_update(
+ &(pImpl->m_context), pImpl->m_cipher.m_mode,
+ rtl_Cipher_DirectionDecode,
+ static_cast<const sal_uInt8*>(pData), nDatLen, pBuffer, nBufLen);
+}
+
+void SAL_CALL rtl_cipher_destroyBF(rtlCipher Cipher) SAL_THROW_EXTERN_C()
+{
+ CipherBF_Impl *pImpl = static_cast<CipherBF_Impl*>(Cipher);
+ if (!pImpl)
+ return;
+
+ if (pImpl->m_cipher.m_algorithm == rtl_Cipher_AlgorithmBF)
+ {
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+ if (pImpl->m_context.m_context != nullptr) {
+ EVP_CIPHER_CTX_free(pImpl->m_context.m_context);
+ }
+#endif
+ rtl_freeZeroMemory(pImpl, sizeof(CipherBF_Impl));
+ }
+ else
+ free(pImpl);
+}
+
+#if !defined LIBO_CIPHER_OPENSSL_BACKEND
+#define CIPHER_CBLOCK_ARCFOUR 256
+#endif
+
+namespace {
+
+struct ContextARCFOUR_Impl
+{
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+ EVP_CIPHER_CTX * m_context;
+#else
+ unsigned int m_S[CIPHER_CBLOCK_ARCFOUR];
+ unsigned int m_X, m_Y;
+#endif
+};
+
+struct CipherARCFOUR_Impl
+{
+ Cipher_Impl m_cipher;
+ ContextARCFOUR_Impl m_context;
+};
+
+}
+
+static rtlCipherError rtl_cipherARCFOUR_update_Impl(
+ ContextARCFOUR_Impl *ctx,
+ const sal_uInt8 *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen);
+
+static rtlCipherError rtl_cipherARCFOUR_init_Impl(
+ ContextARCFOUR_Impl *ctx,
+ const sal_uInt8 *pKeyData, sal_Size nKeyLen)
+{
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+ if (nKeyLen > o3tl::make_unsigned(std::numeric_limits<int>::max())) {
+ return rtl_Cipher_E_BufferSize;
+ }
+ if (ctx->m_context != nullptr) {
+ EVP_CIPHER_CTX_free(ctx->m_context);
+ }
+ ctx->m_context = EVP_CIPHER_CTX_new();
+ if (ctx->m_context == nullptr) {
+ return rtl_Cipher_E_Memory;
+ }
+ if (EVP_CipherInit_ex(ctx->m_context, EVP_rc4(), nullptr, nullptr, nullptr, 0) == 0) {
+ // RC4 en- and decryption is identical, so we can use 0=decrypt regardless of direction,
+ // and thus also support rtl_Cipher_DirectionBoth
+ return rtl_Cipher_E_Unknown;
+ }
+ if (EVP_CIPHER_CTX_set_key_length(ctx->m_context, static_cast<int>(nKeyLen)) == 0) {
+ return rtl_Cipher_E_Unknown;
+ }
+ if (EVP_CipherInit_ex(ctx->m_context, nullptr, nullptr, pKeyData, nullptr, -1) == 0) {
+ return rtl_Cipher_E_Unknown;
+ }
+#else
+ unsigned int K[CIPHER_CBLOCK_ARCFOUR];
+ unsigned int *L, *S;
+ unsigned int x, y;
+ sal_Size n, k;
+
+ S = &(ctx->m_S[0]);
+
+ /* Initialize S linearly. */
+ for (x = 0; x < CIPHER_CBLOCK_ARCFOUR; x++)
+ S[x] = x;
+
+ /* Initialize K with key, repeat key as necessary. */
+ for (L = K, n = CIPHER_CBLOCK_ARCFOUR; n > nKeyLen; n -= nKeyLen)
+ {
+ for (k = 0; k < nKeyLen; k++)
+ {
+ L[k] = pKeyData[k];
+ }
+
+ L += nKeyLen;
+ }
+
+ for (k = 0; k < n; k++)
+ {
+ L[k] = pKeyData[k];
+ }
+
+ /* Initialize S with K. */
+ for (x = 0, y = 0; x < CIPHER_CBLOCK_ARCFOUR; x++)
+ {
+ y = (y + S[x] + K[x]) % CIPHER_CBLOCK_ARCFOUR;
+ /* swap S[x] and S[y] */
+ unsigned int t = S[x];
+ S[x] = S[y];
+ S[y] = t;
+ }
+
+ /* Initialize counters X and Y. */
+ ctx->m_X = 0;
+ ctx->m_Y = 0;
+#endif
+
+ return rtl_Cipher_E_None;
+}
+
+static rtlCipherError rtl_cipherARCFOUR_update_Impl(
+ ContextARCFOUR_Impl *ctx,
+ const sal_uInt8 *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen)
+{
+ /* Check arguments. */
+ if (!pData || !pBuffer)
+ return rtl_Cipher_E_Argument;
+
+ if ((0 >= nDatLen) || (nDatLen > nBufLen))
+ return rtl_Cipher_E_BufferSize;
+
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+ while (nDatLen > o3tl::make_unsigned(std::numeric_limits<int>::max())) {
+ int outl;
+ if (EVP_CipherUpdate(ctx->m_context, pBuffer, &outl, pData, std::numeric_limits<int>::max())
+ == 0)
+ {
+ return rtl_Cipher_E_Unknown;
+ }
+ assert(outl == std::numeric_limits<int>::max());
+ pData += std::numeric_limits<int>::max();
+ nDatLen -= std::numeric_limits<int>::max();
+ pBuffer += std::numeric_limits<int>::max();
+ }
+ int outl;
+ if (EVP_CipherUpdate(ctx->m_context, pBuffer, &outl, pData, static_cast<int>(nDatLen)) == 0) {
+ return rtl_Cipher_E_Unknown;
+ }
+ assert(outl == static_cast<int>(nDatLen));
+ // A final call to EVP_CipherFinal_ex is intentionally missing; it wouldn't fit the rtl/cipher.h
+ // interface, and is hopefully not needed, as each individual RC4 update step doesn't hold back
+ // any data that would need to be finally flushed.
+#else
+ unsigned int *S;
+ sal_Size k;
+
+ /* Update. */
+ S = &(ctx->m_S[0]);
+ for (k = 0; k < nDatLen; k++)
+ {
+ /* Update counters X and Y. */
+ unsigned int x = ctx->m_X;
+ unsigned int y = ctx->m_Y;
+ x = (x + 1 ) % CIPHER_CBLOCK_ARCFOUR;
+ y = (y + S[x]) % CIPHER_CBLOCK_ARCFOUR;
+ ctx->m_X = x;
+ ctx->m_Y = y;
+
+ /* Swap S[x] and S[y]. */
+ unsigned int t = S[x];
+ S[x] = S[y];
+ S[y] = t;
+
+ /* Evaluate next key byte S[t]. */
+ t = (S[x] + S[y]) % CIPHER_CBLOCK_ARCFOUR;
+ pBuffer[k] = pData[k] ^ static_cast<sal_uInt8>(S[t] & 0xff);
+ }
+#endif
+
+ return rtl_Cipher_E_None;
+}
+
+/**
+ rtl_cipher_ARCFOUR (RC4) implementation.
+
+ Reference: Bruce Schneier: Applied Cryptography, 2nd edition, ch. 17.1
+*/
+rtlCipher SAL_CALL rtl_cipher_createARCFOUR(rtlCipherMode Mode)
+ SAL_THROW_EXTERN_C()
+{
+ CipherARCFOUR_Impl *pImpl = nullptr;
+
+ if (Mode != rtl_Cipher_ModeStream)
+ return nullptr;
+
+ pImpl = static_cast<CipherARCFOUR_Impl*>(rtl_allocateZeroMemory(sizeof(CipherARCFOUR_Impl)));
+ if (pImpl)
+ {
+ pImpl->m_cipher.m_algorithm = rtl_Cipher_AlgorithmARCFOUR;
+ pImpl->m_cipher.m_direction = rtl_Cipher_DirectionInvalid;
+ pImpl->m_cipher.m_mode = rtl_Cipher_ModeStream;
+
+ pImpl->m_cipher.m_init = rtl_cipher_initARCFOUR;
+ pImpl->m_cipher.m_encode = rtl_cipher_encodeARCFOUR;
+ pImpl->m_cipher.m_decode = rtl_cipher_decodeARCFOUR;
+ pImpl->m_cipher.m_delete = rtl_cipher_destroyARCFOUR;
+ }
+
+ return static_cast<rtlCipher>(pImpl);
+}
+
+rtlCipherError SAL_CALL rtl_cipher_initARCFOUR(
+ rtlCipher Cipher,
+ rtlCipherDirection Direction,
+ const sal_uInt8 *pKeyData, sal_Size nKeyLen,
+ SAL_UNUSED_PARAMETER const sal_uInt8 *, SAL_UNUSED_PARAMETER sal_Size)
+ SAL_THROW_EXTERN_C()
+{
+ CipherARCFOUR_Impl *pImpl = static_cast<CipherARCFOUR_Impl*>(Cipher);
+
+ if (!pImpl || !pKeyData)
+ return rtl_Cipher_E_Argument;
+
+ if (pImpl->m_cipher.m_algorithm != rtl_Cipher_AlgorithmARCFOUR)
+ return rtl_Cipher_E_Algorithm;
+
+ if (Direction != rtl_Cipher_DirectionInvalid)
+ pImpl->m_cipher.m_direction = Direction;
+ else
+ return rtl_Cipher_E_Direction;
+
+ return rtl_cipherARCFOUR_init_Impl(&(pImpl->m_context), pKeyData, nKeyLen);
+}
+
+rtlCipherError SAL_CALL rtl_cipher_encodeARCFOUR(
+ rtlCipher Cipher,
+ const void *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen) SAL_THROW_EXTERN_C()
+{
+ CipherARCFOUR_Impl *pImpl = static_cast<CipherARCFOUR_Impl*>(Cipher);
+ if (!pImpl)
+ return rtl_Cipher_E_Argument;
+
+ if (pImpl->m_cipher.m_algorithm != rtl_Cipher_AlgorithmARCFOUR)
+ return rtl_Cipher_E_Algorithm;
+
+ if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionInvalid)
+ return rtl_Cipher_E_Direction;
+
+ return rtl_cipherARCFOUR_update_Impl(
+ &(pImpl->m_context),
+ static_cast<const sal_uInt8*>(pData), nDatLen, pBuffer, nBufLen);
+}
+
+rtlCipherError SAL_CALL rtl_cipher_decodeARCFOUR(
+ rtlCipher Cipher,
+ const void *pData, sal_Size nDatLen,
+ sal_uInt8 *pBuffer, sal_Size nBufLen) SAL_THROW_EXTERN_C()
+{
+ CipherARCFOUR_Impl *pImpl = static_cast<CipherARCFOUR_Impl*>(Cipher);
+ if (!pImpl)
+ return rtl_Cipher_E_Argument;
+
+ if (pImpl->m_cipher.m_algorithm != rtl_Cipher_AlgorithmARCFOUR)
+ return rtl_Cipher_E_Algorithm;
+
+ if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionInvalid)
+ return rtl_Cipher_E_Direction;
+
+ return rtl_cipherARCFOUR_update_Impl(
+ &(pImpl->m_context),
+ static_cast<const sal_uInt8*>(pData), nDatLen, pBuffer, nBufLen);
+}
+
+void SAL_CALL rtl_cipher_destroyARCFOUR(rtlCipher Cipher) SAL_THROW_EXTERN_C()
+{
+ CipherARCFOUR_Impl *pImpl = static_cast<CipherARCFOUR_Impl*>(Cipher);
+ if (!pImpl)
+ return;
+
+ if (pImpl->m_cipher.m_algorithm == rtl_Cipher_AlgorithmARCFOUR)
+ {
+#if defined LIBO_CIPHER_OPENSSL_BACKEND
+ if (pImpl->m_context.m_context != nullptr) {
+ EVP_CIPHER_CTX_free(pImpl->m_context.m_context);
+ }
+#endif
+ rtl_freeZeroMemory(pImpl, sizeof(CipherARCFOUR_Impl));
+ }
+ else
+ free(pImpl);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/cmdargs.cxx b/sal/rtl/cmdargs.cxx
new file mode 100644
index 000000000..bad833077
--- /dev/null
+++ b/sal/rtl/cmdargs.cxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/mutex.hxx>
+#include <rtl/alloc.h>
+#include <rtl/process.h>
+#include <rtl/ustring.hxx>
+
+namespace {
+
+rtl_uString ** g_ppCommandArgs = nullptr;
+sal_uInt32 g_nCommandArgCount = 0;
+
+struct ArgHolder
+{
+ ~ArgHolder();
+};
+
+ArgHolder::~ArgHolder()
+{
+ while (g_nCommandArgCount > 0)
+ rtl_uString_release (g_ppCommandArgs[--g_nCommandArgCount]);
+
+ free (g_ppCommandArgs);
+ g_ppCommandArgs = nullptr;
+}
+
+// The destructor of this static ArgHolder is "activated" by the assignments to
+// g_ppCommandArgs and g_nCommandArgCount in init():
+ArgHolder argHolder;
+
+void init()
+{
+ osl::MutexGuard guard( osl::Mutex::getGlobalMutex() );
+ if (g_ppCommandArgs)
+ return;
+
+ sal_Int32 i, n = osl_getCommandArgCount();
+
+ g_ppCommandArgs =
+ static_cast<rtl_uString**>(rtl_allocateZeroMemory (n * sizeof(rtl_uString*)));
+ for (i = 0; i < n; i++)
+ {
+ rtl_uString * pArg = nullptr;
+ osl_getCommandArg (i, &pArg);
+ bool env;
+ {
+ auto const & arg = OUString::unacquired(&pArg);
+ env = (arg.startsWith("-") || arg.startsWith("/")) &&
+ arg.match("env:", 1) &&
+ arg.indexOf ('=') >= 0;
+ }
+ if (env )
+ {
+ // ignore.
+ rtl_uString_release (pArg);
+ }
+ else
+ {
+ // assign.
+ g_ppCommandArgs[g_nCommandArgCount++] = pArg;
+ }
+ }
+}
+
+}
+
+oslProcessError SAL_CALL rtl_getAppCommandArg (
+ sal_uInt32 nArg, rtl_uString **ppCommandArg)
+{
+ init();
+ oslProcessError result = osl_Process_E_NotFound;
+ if( nArg < g_nCommandArgCount )
+ {
+ rtl_uString_assign( ppCommandArg, g_ppCommandArgs[nArg] );
+ result = osl_Process_E_None;
+ }
+ return result;
+}
+
+sal_uInt32 SAL_CALL rtl_getAppCommandArgCount()
+{
+ init();
+ return g_nCommandArgCount;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/crc.cxx b/sal/rtl/crc.cxx
new file mode 100644
index 000000000..7912b15d0
--- /dev/null
+++ b/sal/rtl/crc.cxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/types.h>
+#include <rtl/crc.h>
+#include <cassert>
+
+#include <zlib.h>
+
+// Zlib's crc32() is very fast, so simply use that one instead
+// of implementing this ourselves.
+
+sal_uInt32 SAL_CALL rtl_crc32 (
+ sal_uInt32 Crc,
+ const void *Data, sal_uInt32 DatLen) SAL_THROW_EXTERN_C()
+{
+ // Check that our types map to zlib correctly.
+ static_assert(sizeof(uLong) >= sizeof(sal_uInt32));
+ static_assert(sizeof(uInt) >= sizeof(sal_uInt32));
+ // Our API says that Crc should be initialized to 0, while
+ // zlib says it should be initialized with 'crc32(0,Z_NULL,0)',
+ // which however simply returns 0. Ensure this.
+ assert(crc32(0, Z_NULL, 0) == 0);
+ if (Data)
+ Crc = crc32( Crc, static_cast<const Bytef*>(Data), DatLen);
+ return Crc;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/digest.cxx b/sal/rtl/digest.cxx
new file mode 100644
index 000000000..de52ecae5
--- /dev/null
+++ b/sal/rtl/digest.cxx
@@ -0,0 +1,1897 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <sal/types.h>
+#include <osl/endian.h>
+#include <rtl/alloc.h>
+#include <rtl/digest.h>
+
+#define RTL_DIGEST_CREATE(T) (static_cast<T*>(rtl_allocateZeroMemory(sizeof(T))))
+
+#define RTL_DIGEST_ROTL(a,n) (((a) << (n)) | ((a) >> (32 - (n))))
+
+#define RTL_DIGEST_HTONL(l,c) \
+ (*((c)++) = static_cast<sal_uInt8>(((l) >> 24) & 0xff), \
+ *((c)++) = static_cast<sal_uInt8>(((l) >> 16) & 0xff), \
+ *((c)++) = static_cast<sal_uInt8>(((l) >> 8) & 0xff), \
+ *((c)++) = static_cast<sal_uInt8>(((l) ) & 0xff))
+
+#define RTL_DIGEST_LTOC(l,c) \
+ *((c)++) = static_cast<sal_uInt8>(((l) ) & 0xff); \
+ *((c)++) = static_cast<sal_uInt8>(((l) >> 8) & 0xff); \
+ *((c)++) = static_cast<sal_uInt8>(((l) >> 16) & 0xff); \
+ *((c)++) = static_cast<sal_uInt8>(((l) >> 24) & 0xff);
+
+typedef rtlDigestError (Digest_init_t) (
+ void *ctx, const sal_uInt8 *Data, sal_uInt32 DatLen);
+
+typedef void (Digest_delete_t) (void *ctx);
+
+typedef rtlDigestError (Digest_update_t) (
+ void *ctx, const void *Data, sal_uInt32 DatLen);
+
+typedef rtlDigestError (Digest_get_t) (
+ void *ctx, sal_uInt8 *Buffer, sal_uInt32 BufLen);
+
+namespace {
+
+struct Digest_Impl
+{
+ rtlDigestAlgorithm m_algorithm;
+ sal_uInt32 m_length;
+
+ Digest_init_t *m_init;
+ Digest_delete_t *m_delete;
+ Digest_update_t *m_update;
+ Digest_get_t *m_get;
+};
+
+}
+
+static void swapLong(sal_uInt32 *pData, sal_uInt32 nDatLen)
+{
+ sal_uInt32 *X;
+ int i, n;
+
+ X = pData;
+ n = nDatLen;
+
+ for (i = 0; i < n; i++)
+ {
+ X[i] = OSL_SWAPDWORD(X[i]);
+ }
+}
+
+rtlDigest SAL_CALL rtl_digest_create(rtlDigestAlgorithm Algorithm)
+ SAL_THROW_EXTERN_C()
+{
+ rtlDigest Digest = nullptr;
+ switch (Algorithm)
+ {
+ case rtl_Digest_AlgorithmMD2:
+ Digest = rtl_digest_createMD2();
+ break;
+
+ case rtl_Digest_AlgorithmMD5:
+ Digest = rtl_digest_createMD5();
+ break;
+
+ case rtl_Digest_AlgorithmSHA:
+ Digest = rtl_digest_createSHA();
+ break;
+
+ case rtl_Digest_AlgorithmSHA1:
+ Digest = rtl_digest_createSHA1();
+ break;
+
+ case rtl_Digest_AlgorithmHMAC_MD5:
+ Digest = rtl_digest_createHMAC_MD5();
+ break;
+
+ case rtl_Digest_AlgorithmHMAC_SHA1:
+ Digest = rtl_digest_createHMAC_SHA1();
+ break;
+
+ default: /* rtl_Digest_AlgorithmInvalid */
+ break;
+ }
+ return Digest;
+}
+
+rtlDigestAlgorithm SAL_CALL rtl_digest_queryAlgorithm(rtlDigest Digest)
+ SAL_THROW_EXTERN_C()
+{
+ Digest_Impl *pImpl = static_cast<Digest_Impl *>(Digest);
+ if (pImpl)
+ return pImpl->m_algorithm;
+ return rtl_Digest_AlgorithmInvalid;
+}
+
+sal_uInt32 SAL_CALL rtl_digest_queryLength(rtlDigest Digest)
+ SAL_THROW_EXTERN_C()
+{
+ Digest_Impl *pImpl = static_cast<Digest_Impl *>(Digest);
+ if (pImpl)
+ return pImpl->m_length;
+ return 0;
+}
+
+rtlDigestError SAL_CALL rtl_digest_init(
+ rtlDigest Digest, const sal_uInt8 *pData, sal_uInt32 nDatLen)
+ SAL_THROW_EXTERN_C()
+{
+ Digest_Impl *pImpl = static_cast<Digest_Impl *>(Digest);
+ if (pImpl)
+ {
+ if (pImpl->m_init)
+ return pImpl->m_init (Digest, pData, nDatLen);
+ return rtl_Digest_E_None;
+ }
+ return rtl_Digest_E_Argument;
+}
+
+rtlDigestError SAL_CALL rtl_digest_update(
+ rtlDigest Digest, const void *pData, sal_uInt32 nDatLen)
+ SAL_THROW_EXTERN_C()
+{
+ Digest_Impl *pImpl = static_cast<Digest_Impl *>(Digest);
+ if (pImpl && pImpl->m_update)
+ return pImpl->m_update(Digest, pData, nDatLen);
+ return rtl_Digest_E_Argument;
+}
+
+rtlDigestError SAL_CALL rtl_digest_get(
+ rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen)
+ SAL_THROW_EXTERN_C()
+{
+ Digest_Impl *pImpl = static_cast<Digest_Impl *>(Digest);
+ if (pImpl && pImpl->m_get)
+ return pImpl->m_get(Digest, pBuffer, nBufLen);
+ return rtl_Digest_E_Argument;
+}
+
+void SAL_CALL rtl_digest_destroy(rtlDigest Digest) SAL_THROW_EXTERN_C()
+{
+ Digest_Impl *pImpl = static_cast<Digest_Impl *>(Digest);
+ if (pImpl && pImpl->m_delete)
+ pImpl->m_delete(Digest);
+}
+
+constexpr auto DIGEST_CBLOCK_MD2 = 16;
+constexpr auto DIGEST_LBLOCK_MD2 = 16;
+
+namespace {
+
+struct DigestContextMD2
+{
+ sal_uInt32 m_nDatLen;
+ sal_uInt8 m_pData[DIGEST_CBLOCK_MD2];
+ sal_uInt32 m_state[DIGEST_LBLOCK_MD2];
+ sal_uInt32 m_chksum[DIGEST_LBLOCK_MD2];
+};
+
+struct DigestMD2_Impl
+{
+ Digest_Impl m_digest;
+ DigestContextMD2 m_context;
+};
+
+}
+
+static void initMD2 (DigestContextMD2 *ctx);
+static void updateMD2 (DigestContextMD2 *ctx);
+static void endMD2 (DigestContextMD2 *ctx);
+
+const sal_uInt32 S[256] =
+{
+ 0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01,
+ 0x3D, 0x36, 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13,
+ 0x62, 0xA7, 0x05, 0xF3, 0xC0, 0xC7, 0x73, 0x8C,
+ 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, 0x82, 0xCA,
+ 0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16,
+ 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12,
+ 0xBE, 0x4E, 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49,
+ 0xA0, 0xFB, 0xF5, 0x8E, 0xBB, 0x2F, 0xEE, 0x7A,
+ 0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, 0x07, 0x3F,
+ 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21,
+ 0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27,
+ 0x35, 0x3E, 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03,
+ 0xFF, 0x19, 0x30, 0xB3, 0x48, 0xA5, 0xB5, 0xD1,
+ 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, 0xAA, 0xC6,
+ 0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6,
+ 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1,
+ 0x45, 0x9D, 0x70, 0x59, 0x64, 0x71, 0x87, 0x20,
+ 0x86, 0x5B, 0xCF, 0x65, 0xE6, 0x2D, 0xA8, 0x02,
+ 0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, 0xB9, 0xF6,
+ 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F,
+ 0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A,
+ 0xC3, 0x5C, 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26,
+ 0x2C, 0x53, 0x0D, 0x6E, 0x85, 0x28, 0x84, 0x09,
+ 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, 0x4D, 0x52,
+ 0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA,
+ 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A,
+ 0x78, 0x88, 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D,
+ 0xE9, 0xCB, 0xD5, 0xFE, 0x3B, 0x00, 0x1D, 0x39,
+ 0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, 0xD0, 0xE4,
+ 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A,
+ 0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A,
+ 0xDB, 0x99, 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14,
+};
+
+const Digest_Impl MD2 =
+{
+ rtl_Digest_AlgorithmMD2,
+ RTL_DIGEST_LENGTH_MD2,
+ nullptr,
+ rtl_digest_destroyMD2,
+ rtl_digest_updateMD2,
+ rtl_digest_getMD2
+};
+
+static void initMD2(DigestContextMD2 *ctx)
+{
+ memset(ctx, 0, sizeof(DigestContextMD2));
+}
+
+static void updateMD2(DigestContextMD2 *ctx)
+{
+ sal_uInt8 *X;
+ sal_uInt32 *sp1, *sp2;
+ sal_uInt32 i, k, t;
+
+ sal_uInt32 state[48];
+
+ X = ctx->m_pData;
+ sp1 = ctx->m_state;
+ sp2 = ctx->m_chksum;
+
+ k = sp2[DIGEST_LBLOCK_MD2 - 1];
+ for (i = 0; i < 16; i++)
+ {
+ state[i + 0] = sp1[i];
+ state[i + 16] = t = X[i];
+ state[i + 32] = t ^ sp1[i];
+ k = sp2[i] ^= S[t^k];
+ }
+
+ t = 0;
+ for (i = 0; i < 18; i++)
+ {
+ for (k = 0; k < 48; k += 8)
+ {
+ t = state[k + 0] ^= S[t];
+ t = state[k + 1] ^= S[t];
+ t = state[k + 2] ^= S[t];
+ t = state[k + 3] ^= S[t];
+ t = state[k + 4] ^= S[t];
+ t = state[k + 5] ^= S[t];
+ t = state[k + 6] ^= S[t];
+ t = state[k + 7] ^= S[t];
+ }
+ t = ((t + i) & 0xff);
+ }
+
+ memcpy(sp1, state, 16 * sizeof(sal_uInt32));
+ rtl_secureZeroMemory(state, 48 * sizeof(sal_uInt32));
+}
+
+static void endMD2(DigestContextMD2 *ctx)
+{
+ sal_uInt8 *X;
+ sal_uInt32 *C;
+ sal_uInt32 i, n;
+
+ X = ctx->m_pData;
+ C = ctx->m_chksum;
+ n = DIGEST_CBLOCK_MD2 - ctx->m_nDatLen;
+
+ for (i = ctx->m_nDatLen; i < DIGEST_CBLOCK_MD2; i++)
+ X[i] = static_cast<sal_uInt8>(n & 0xff);
+
+ updateMD2(ctx);
+
+ for (i = 0; i < DIGEST_CBLOCK_MD2; i++)
+ X[i] = static_cast<sal_uInt8>(C[i] & 0xff);
+ updateMD2(ctx);
+}
+
+rtlDigestError SAL_CALL rtl_digest_MD2(
+ const void *pData, sal_uInt32 nDatLen,
+ sal_uInt8 *pBuffer, sal_uInt32 nBufLen) SAL_THROW_EXTERN_C()
+{
+ DigestMD2_Impl digest;
+ rtlDigestError result;
+
+ digest.m_digest = MD2;
+ initMD2(&(digest.m_context));
+
+ result = rtl_digest_updateMD2(&digest, pData, nDatLen);
+ if (result == rtl_Digest_E_None)
+ result = rtl_digest_getMD2(&digest, pBuffer, nBufLen);
+
+ rtl_secureZeroMemory(&digest, sizeof(digest));
+ return result;
+}
+
+rtlDigest SAL_CALL rtl_digest_createMD2() SAL_THROW_EXTERN_C()
+{
+ DigestMD2_Impl *pImpl = RTL_DIGEST_CREATE(DigestMD2_Impl);
+ if (pImpl)
+ {
+ pImpl->m_digest = MD2;
+ initMD2(&(pImpl->m_context));
+ }
+ return static_cast<rtlDigest>(pImpl);
+}
+
+rtlDigestError SAL_CALL rtl_digest_updateMD2(
+ rtlDigest Digest, const void *pData, sal_uInt32 nDatLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestMD2_Impl *pImpl = static_cast<DigestMD2_Impl *>(Digest);
+ const sal_uInt8 *d = static_cast<const sal_uInt8 *>(pData);
+
+ DigestContextMD2 *ctx;
+
+ if (!pImpl || !pData)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmMD2)
+ return rtl_Digest_E_Algorithm;
+
+ if (nDatLen == 0)
+ return rtl_Digest_E_None;
+
+ ctx = &(pImpl->m_context);
+
+ if (ctx->m_nDatLen)
+ {
+ sal_uInt8 *p = ctx->m_pData + ctx->m_nDatLen;
+ sal_uInt32 n = DIGEST_CBLOCK_MD2 - ctx->m_nDatLen;
+
+ if (nDatLen < n)
+ {
+ memcpy(p, d, nDatLen);
+ ctx->m_nDatLen += nDatLen;
+
+ return rtl_Digest_E_None;
+ }
+
+ memcpy(p, d, n);
+ d += n;
+ nDatLen -= n;
+
+ updateMD2(ctx);
+ ctx->m_nDatLen = 0;
+ }
+
+ while (nDatLen >= DIGEST_CBLOCK_MD2)
+ {
+ memcpy(ctx->m_pData, d, DIGEST_CBLOCK_MD2);
+ d += DIGEST_CBLOCK_MD2;
+ nDatLen -= DIGEST_CBLOCK_MD2;
+
+ updateMD2(ctx);
+ }
+
+ memcpy(ctx->m_pData, d, nDatLen);
+ ctx->m_nDatLen = nDatLen;
+
+ return rtl_Digest_E_None;
+}
+
+rtlDigestError SAL_CALL rtl_digest_getMD2(
+ rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestMD2_Impl *pImpl = static_cast<DigestMD2_Impl *>(Digest);
+ sal_uInt32 i;
+
+ DigestContextMD2 *ctx;
+
+ if (!pImpl || !pBuffer)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmMD2)
+ return rtl_Digest_E_Algorithm;
+
+ if (pImpl->m_digest.m_length > nBufLen)
+ return rtl_Digest_E_BufferSize;
+
+ ctx = &(pImpl->m_context);
+
+ endMD2(ctx);
+ for (i = 0; i < DIGEST_CBLOCK_MD2; i++)
+ {
+ pBuffer[i] = static_cast<sal_uInt8>(ctx->m_state[i] & 0xff);
+ }
+
+ initMD2(ctx);
+
+ return rtl_Digest_E_None;
+}
+
+void SAL_CALL rtl_digest_destroyMD2(rtlDigest Digest) SAL_THROW_EXTERN_C()
+{
+ DigestMD2_Impl *pImpl = static_cast<DigestMD2_Impl *>(Digest);
+ if (pImpl)
+ {
+ if (pImpl->m_digest.m_algorithm == rtl_Digest_AlgorithmMD2)
+ rtl_freeZeroMemory(pImpl, sizeof(DigestMD2_Impl));
+ else
+ free(pImpl);
+ }
+}
+
+#define DIGEST_CBLOCK_MD5 64
+#define DIGEST_LBLOCK_MD5 16
+
+namespace {
+
+struct DigestContextMD5
+{
+ sal_uInt32 m_nDatLen;
+ sal_uInt32 m_pData[DIGEST_LBLOCK_MD5];
+ sal_uInt32 m_nA, m_nB, m_nC, m_nD;
+ sal_uInt32 m_nL, m_nH;
+};
+
+struct DigestMD5_Impl
+{
+ Digest_Impl m_digest;
+ DigestContextMD5 m_context;
+};
+
+}
+
+static void initMD5 (DigestContextMD5 *ctx);
+static void updateMD5 (DigestContextMD5 *ctx);
+static void endMD5 (DigestContextMD5 *ctx);
+
+#define F(x,y,z) ((((y) ^ (z)) & (x)) ^ (z))
+#define G(x,y,z) ((((x) ^ (y)) & (z)) ^ (y))
+#define H(x,y,z) ((x) ^ (y) ^ (z))
+#define I(x,y,z) (((x) | (~(z))) ^ (y))
+
+#define R0(a,b,c,d,k,s,t) { \
+ a += ((k) + (t) + F((b), (c), (d))); \
+ a = RTL_DIGEST_ROTL(a, s); \
+ a += b; }
+
+#define R1(a,b,c,d,k,s,t) { \
+ a += ((k) + (t) + G((b), (c), (d))); \
+ a = RTL_DIGEST_ROTL(a, s); \
+ a += b; }
+
+#define R2(a,b,c,d,k,s,t) { \
+ a += ((k) + (t) + H((b), (c), (d))); \
+ a = RTL_DIGEST_ROTL(a, s); \
+ a += b; }
+
+#define R3(a,b,c,d,k,s,t) { \
+ a += ((k) + (t) + I((b), (c), (d))); \
+ a = RTL_DIGEST_ROTL(a, s); \
+ a += b; }
+
+const Digest_Impl MD5 =
+{
+ rtl_Digest_AlgorithmMD5,
+ RTL_DIGEST_LENGTH_MD5,
+ nullptr,
+ rtl_digest_destroyMD5,
+ rtl_digest_updateMD5,
+ rtl_digest_getMD5
+};
+
+static void initMD5(DigestContextMD5 *ctx)
+{
+ memset(ctx, 0, sizeof(DigestContextMD5));
+
+ ctx->m_nA = sal_uInt32(0x67452301L);
+ ctx->m_nB = sal_uInt32(0xefcdab89L);
+ ctx->m_nC = sal_uInt32(0x98badcfeL);
+ ctx->m_nD = sal_uInt32(0x10325476L);
+}
+
+static void updateMD5(DigestContextMD5 *ctx)
+{
+ sal_uInt32 A, B, C, D;
+ sal_uInt32 *X;
+
+ A = ctx->m_nA;
+ B = ctx->m_nB;
+ C = ctx->m_nC;
+ D = ctx->m_nD;
+ X = ctx->m_pData;
+
+ R0 (A, B, C, D, X[ 0], 7, 0xd76aa478L);
+ R0 (D, A, B, C, X[ 1], 12, 0xe8c7b756L);
+ R0 (C, D, A, B, X[ 2], 17, 0x242070dbL);
+ R0 (B, C, D, A, X[ 3], 22, 0xc1bdceeeL);
+ R0 (A, B, C, D, X[ 4], 7, 0xf57c0fafL);
+ R0 (D, A, B, C, X[ 5], 12, 0x4787c62aL);
+ R0 (C, D, A, B, X[ 6], 17, 0xa8304613L);
+ R0 (B, C, D, A, X[ 7], 22, 0xfd469501L);
+ R0 (A, B, C, D, X[ 8], 7, 0x698098d8L);
+ R0 (D, A, B, C, X[ 9], 12, 0x8b44f7afL);
+ R0 (C, D, A, B, X[10], 17, 0xffff5bb1L);
+ R0 (B, C, D, A, X[11], 22, 0x895cd7beL);
+ R0 (A, B, C, D, X[12], 7, 0x6b901122L);
+ R0 (D, A, B, C, X[13], 12, 0xfd987193L);
+ R0 (C, D, A, B, X[14], 17, 0xa679438eL);
+ R0 (B, C, D, A, X[15], 22, 0x49b40821L);
+
+ R1 (A, B, C, D, X[ 1], 5, 0xf61e2562L);
+ R1 (D, A, B, C, X[ 6], 9, 0xc040b340L);
+ R1 (C, D, A, B, X[11], 14, 0x265e5a51L);
+ R1 (B, C, D, A, X[ 0], 20, 0xe9b6c7aaL);
+ R1 (A, B, C, D, X[ 5], 5, 0xd62f105dL);
+ R1 (D, A, B, C, X[10], 9, 0x02441453L);
+ R1 (C, D, A, B, X[15], 14, 0xd8a1e681L);
+ R1 (B, C, D, A, X[ 4], 20, 0xe7d3fbc8L);
+ R1 (A, B, C, D, X[ 9], 5, 0x21e1cde6L);
+ R1 (D, A, B, C, X[14], 9, 0xc33707d6L);
+ R1 (C, D, A, B, X[ 3], 14, 0xf4d50d87L);
+ R1 (B, C, D, A, X[ 8], 20, 0x455a14edL);
+ R1 (A, B, C, D, X[13], 5, 0xa9e3e905L);
+ R1 (D, A, B, C, X[ 2], 9, 0xfcefa3f8L);
+ R1 (C, D, A, B, X[ 7], 14, 0x676f02d9L);
+ R1 (B, C, D, A, X[12], 20, 0x8d2a4c8aL);
+
+ R2 (A, B, C, D, X[ 5], 4, 0xfffa3942L);
+ R2 (D, A, B, C, X[ 8], 11, 0x8771f681L);
+ R2 (C, D, A, B, X[11], 16, 0x6d9d6122L);
+ R2 (B, C, D, A, X[14], 23, 0xfde5380cL);
+ R2 (A, B, C, D, X[ 1], 4, 0xa4beea44L);
+ R2 (D, A, B, C, X[ 4], 11, 0x4bdecfa9L);
+ R2 (C, D, A, B, X[ 7], 16, 0xf6bb4b60L);
+ R2 (B, C, D, A, X[10], 23, 0xbebfbc70L);
+ R2 (A, B, C, D, X[13], 4, 0x289b7ec6L);
+ R2 (D, A, B, C, X[ 0], 11, 0xeaa127faL);
+ R2 (C, D, A, B, X[ 3], 16, 0xd4ef3085L);
+ R2 (B, C, D, A, X[ 6], 23, 0x04881d05L);
+ R2 (A, B, C, D, X[ 9], 4, 0xd9d4d039L);
+ R2 (D, A, B, C, X[12], 11, 0xe6db99e5L);
+ R2 (C, D, A, B, X[15], 16, 0x1fa27cf8L);
+ R2 (B, C, D, A, X[ 2], 23, 0xc4ac5665L);
+
+ R3 (A, B, C, D, X[ 0], 6, 0xf4292244L);
+ R3 (D, A, B, C, X[ 7], 10, 0x432aff97L);
+ R3 (C, D, A, B, X[14], 15, 0xab9423a7L);
+ R3 (B, C, D, A, X[ 5], 21, 0xfc93a039L);
+ R3 (A, B, C, D, X[12], 6, 0x655b59c3L);
+ R3 (D, A, B, C, X[ 3], 10, 0x8f0ccc92L);
+ R3 (C, D, A, B, X[10], 15, 0xffeff47dL);
+ R3 (B, C, D, A, X[ 1], 21, 0x85845dd1L);
+ R3 (A, B, C, D, X[ 8], 6, 0x6fa87e4fL);
+ R3 (D, A, B, C, X[15], 10, 0xfe2ce6e0L);
+ R3 (C, D, A, B, X[ 6], 15, 0xa3014314L);
+ R3 (B, C, D, A, X[13], 21, 0x4e0811a1L);
+ R3 (A, B, C, D, X[ 4], 6, 0xf7537e82L);
+ R3 (D, A, B, C, X[11], 10, 0xbd3af235L);
+ R3 (C, D, A, B, X[ 2], 15, 0x2ad7d2bbL);
+ R3 (B, C, D, A, X[ 9], 21, 0xeb86d391L);
+
+ ctx->m_nA += A;
+ ctx->m_nB += B;
+ ctx->m_nC += C;
+ ctx->m_nD += D;
+}
+
+static void endMD5(DigestContextMD5 *ctx)
+{
+ static const sal_uInt8 end[4] =
+ {
+ 0x80, 0x00, 0x00, 0x00
+ };
+ const sal_uInt8 *p = end;
+
+ sal_uInt32 *X;
+ int i;
+
+ X = ctx->m_pData;
+ i = (ctx->m_nDatLen >> 2);
+
+#ifdef OSL_BIGENDIAN
+ swapLong(X, i + 1);
+#endif /* OSL_BIGENDIAN */
+
+ switch (ctx->m_nDatLen & 0x03)
+ {
+ case 1: X[i] &= 0x000000ff; break;
+ case 2: X[i] &= 0x0000ffff; break;
+ case 3: X[i] &= 0x00ffffff; break;
+ }
+
+ switch (ctx->m_nDatLen & 0x03)
+ {
+ case 0: X[i] = static_cast<sal_uInt32>(*(p++)) << 0;
+ [[fallthrough]];
+ case 1: X[i] |= static_cast<sal_uInt32>(*(p++)) << 8;
+ [[fallthrough]];
+ case 2: X[i] |= static_cast<sal_uInt32>(*(p++)) << 16;
+ [[fallthrough]];
+ case 3: X[i] |= static_cast<sal_uInt32>(*p) << 24;
+ }
+
+ i += 1;
+
+ if (i > (DIGEST_LBLOCK_MD5 - 2))
+ {
+ for (; i < DIGEST_LBLOCK_MD5; i++)
+ {
+ X[i] = 0;
+ }
+
+ updateMD5(ctx);
+ i = 0;
+ }
+
+ for (; i < (DIGEST_LBLOCK_MD5 - 2); i++)
+ X[i] = 0;
+
+ X[DIGEST_LBLOCK_MD5 - 2] = ctx->m_nL;
+ X[DIGEST_LBLOCK_MD5 - 1] = ctx->m_nH;
+
+ updateMD5(ctx);
+}
+
+rtlDigestError SAL_CALL rtl_digest_MD5(
+ const void *pData, sal_uInt32 nDatLen,
+ sal_uInt8 *pBuffer, sal_uInt32 nBufLen) SAL_THROW_EXTERN_C()
+{
+ DigestMD5_Impl digest;
+ rtlDigestError result;
+
+ digest.m_digest = MD5;
+ initMD5(&(digest.m_context));
+
+ result = rtl_digest_update(&digest, pData, nDatLen);
+ if (result == rtl_Digest_E_None)
+ result = rtl_digest_getMD5(&digest, pBuffer, nBufLen);
+
+ rtl_secureZeroMemory(&digest, sizeof(digest));
+ return result;
+}
+
+rtlDigest SAL_CALL rtl_digest_createMD5() SAL_THROW_EXTERN_C()
+{
+ DigestMD5_Impl *pImpl = RTL_DIGEST_CREATE(DigestMD5_Impl);
+ if (pImpl)
+ {
+ pImpl->m_digest = MD5;
+ initMD5(&(pImpl->m_context));
+ }
+ return static_cast<rtlDigest>(pImpl);
+}
+
+rtlDigestError SAL_CALL rtl_digest_updateMD5(
+ rtlDigest Digest, const void *pData, sal_uInt32 nDatLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestMD5_Impl *pImpl = static_cast<DigestMD5_Impl *>(Digest);
+ const sal_uInt8 *d = static_cast<const sal_uInt8 *>(pData);
+
+ DigestContextMD5 *ctx;
+ sal_uInt32 len;
+
+ if (!pImpl || !pData)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmMD5)
+ return rtl_Digest_E_Algorithm;
+
+ if (nDatLen == 0)
+ return rtl_Digest_E_None;
+
+ ctx = &(pImpl->m_context);
+
+ len = ctx->m_nL + (nDatLen << 3);
+ if (len < ctx->m_nL)
+ ctx->m_nH += 1;
+
+ ctx->m_nH += (nDatLen >> 29);
+ ctx->m_nL = len;
+
+ if (ctx->m_nDatLen)
+ {
+ sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(ctx->m_pData) + ctx->m_nDatLen;
+ sal_uInt32 n = DIGEST_CBLOCK_MD5 - ctx->m_nDatLen;
+
+ if (nDatLen < n)
+ {
+ memcpy(p, d, nDatLen);
+ ctx->m_nDatLen += nDatLen;
+
+ return rtl_Digest_E_None;
+ }
+
+ memcpy(p, d, n);
+ d += n;
+ nDatLen -= n;
+
+#ifdef OSL_BIGENDIAN
+ swapLong(ctx->m_pData, DIGEST_LBLOCK_MD5);
+#endif /* OSL_BIGENDIAN */
+
+ updateMD5(ctx);
+ ctx->m_nDatLen = 0;
+ }
+
+ while (nDatLen >= DIGEST_CBLOCK_MD5)
+ {
+ memcpy(ctx->m_pData, d, DIGEST_CBLOCK_MD5);
+ d += DIGEST_CBLOCK_MD5;
+ nDatLen -= DIGEST_CBLOCK_MD5;
+
+#ifdef OSL_BIGENDIAN
+ swapLong(ctx->m_pData, DIGEST_LBLOCK_MD5);
+#endif /* OSL_BIGENDIAN */
+
+ updateMD5(ctx);
+ }
+
+ memcpy(ctx->m_pData, d, nDatLen);
+ ctx->m_nDatLen = nDatLen;
+
+ return rtl_Digest_E_None;
+}
+
+rtlDigestError SAL_CALL rtl_digest_getMD5(
+ rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestMD5_Impl *pImpl = static_cast<DigestMD5_Impl *>(Digest);
+ sal_uInt8 *p = pBuffer;
+
+ DigestContextMD5 *ctx;
+
+ if (!pImpl || !pBuffer)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmMD5)
+ return rtl_Digest_E_Algorithm;
+
+ if (pImpl->m_digest.m_length > nBufLen)
+ return rtl_Digest_E_BufferSize;
+
+ ctx = &(pImpl->m_context);
+
+ endMD5(ctx);
+ RTL_DIGEST_LTOC(ctx->m_nA, p);
+ RTL_DIGEST_LTOC(ctx->m_nB, p);
+ RTL_DIGEST_LTOC(ctx->m_nC, p);
+ RTL_DIGEST_LTOC(ctx->m_nD, p);
+ initMD5(ctx);
+
+ return rtl_Digest_E_None;
+}
+
+rtlDigestError SAL_CALL rtl_digest_rawMD5(
+ rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestMD5_Impl *pImpl = static_cast<DigestMD5_Impl *>(Digest);
+ sal_uInt8 *p = pBuffer;
+
+ DigestContextMD5 *ctx;
+
+ if (!pImpl || !pBuffer)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmMD5)
+ return rtl_Digest_E_Algorithm;
+
+ if (pImpl->m_digest.m_length > nBufLen)
+ return rtl_Digest_E_BufferSize;
+
+ ctx = &(pImpl->m_context);
+
+ /* not finalized */
+ RTL_DIGEST_LTOC(ctx->m_nA, p);
+ RTL_DIGEST_LTOC(ctx->m_nB, p);
+ RTL_DIGEST_LTOC(ctx->m_nC, p);
+ RTL_DIGEST_LTOC(ctx->m_nD, p);
+ initMD5(ctx);
+
+ return rtl_Digest_E_None;
+}
+
+void SAL_CALL rtl_digest_destroyMD5(rtlDigest Digest) SAL_THROW_EXTERN_C()
+{
+ DigestMD5_Impl *pImpl = static_cast<DigestMD5_Impl *>(Digest);
+ if (pImpl)
+ {
+ if (pImpl->m_digest.m_algorithm == rtl_Digest_AlgorithmMD5)
+ rtl_freeZeroMemory(pImpl, sizeof(DigestMD5_Impl));
+ else
+ free(pImpl);
+ }
+}
+
+#define DIGEST_CBLOCK_SHA 64
+#define DIGEST_LBLOCK_SHA 16
+
+typedef sal_uInt32 DigestSHA_update_t(sal_uInt32 x);
+
+static sal_uInt32 updateSHA_0(sal_uInt32 x);
+static sal_uInt32 updateSHA_1(sal_uInt32 x);
+
+namespace {
+
+struct DigestContextSHA
+{
+ DigestSHA_update_t *m_update;
+ sal_uInt32 m_nDatLen;
+ sal_uInt32 m_pData[DIGEST_LBLOCK_SHA];
+ sal_uInt32 m_nA, m_nB, m_nC, m_nD, m_nE;
+ sal_uInt32 m_nL, m_nH;
+};
+
+struct DigestSHA_Impl
+{
+ Digest_Impl m_digest;
+ DigestContextSHA m_context;
+};
+
+}
+
+static void initSHA(
+ DigestContextSHA *ctx, DigestSHA_update_t *fct);
+
+static void updateSHA(DigestContextSHA *ctx);
+static void endSHA(DigestContextSHA *ctx);
+
+#define K_00_19 sal_uInt32(0x5a827999L)
+#define K_20_39 sal_uInt32(0x6ed9eba1L)
+#define K_40_59 sal_uInt32(0x8f1bbcdcL)
+#define K_60_79 sal_uInt32(0xca62c1d6L)
+
+#define F_00_19(b,c,d) ((((c) ^ (d)) & (b)) ^ (d))
+#define F_20_39(b,c,d) ((b) ^ (c) ^ (d))
+#define F_40_59(b,c,d) (((b) & (c)) | ((b) & (d)) | ((c) & (d)))
+#define F_60_79(b,c,d) F_20_39(b,c,d)
+
+#define BODY_X(i) \
+ (X[(i)&0x0f] ^ X[((i)+2)&0x0f] ^ X[((i)+8)&0x0f] ^ X[((i)+13)&0x0f])
+
+#define BODY_00_15(u,i,a,b,c,d,e,f) \
+ (f) = X[i]; \
+ (f) += (e) + K_00_19 + RTL_DIGEST_ROTL((a), 5) + F_00_19((b), (c), (d)); \
+ (b) = RTL_DIGEST_ROTL((b), 30);
+
+#define BODY_16_19(u,i,a,b,c,d,e,f) \
+ (f) = BODY_X((i)); \
+ (f) = X[(i)&0x0f] = (u)((f)); \
+ (f) += (e) + K_00_19 + RTL_DIGEST_ROTL((a), 5) + F_00_19((b), (c), (d)); \
+ (b) = RTL_DIGEST_ROTL((b), 30);
+
+#define BODY_20_39(u,i,a,b,c,d,e,f) \
+ (f) = BODY_X((i)); \
+ (f) = X[(i)&0x0f] = (u)((f)); \
+ (f) += (e) + K_20_39 + RTL_DIGEST_ROTL((a), 5) + F_20_39((b), (c), (d)); \
+ (b) = RTL_DIGEST_ROTL((b), 30);
+
+#define BODY_40_59(u,i,a,b,c,d,e,f) \
+ (f) = BODY_X((i)); \
+ (f) = X[(i)&0x0f] = (u)((f)); \
+ (f) += (e) + K_40_59 + RTL_DIGEST_ROTL((a), 5) + F_40_59((b), (c), (d)); \
+ (b) = RTL_DIGEST_ROTL((b), 30);
+
+#define BODY_60_79(u,i,a,b,c,d,e,f) \
+ (f) = BODY_X((i)); \
+ (f) = X[(i)&0x0f] = (u)((f)); \
+ (f) += (e) + K_60_79 + RTL_DIGEST_ROTL((a), 5) + F_60_79((b), (c), (d)); \
+ (b) = RTL_DIGEST_ROTL((b), 30);
+
+static void initSHA(
+ DigestContextSHA *ctx, DigestSHA_update_t *fct)
+{
+ memset(ctx, 0, sizeof(DigestContextSHA));
+ ctx->m_update = fct;
+
+ ctx->m_nA = sal_uInt32(0x67452301L);
+ ctx->m_nB = sal_uInt32(0xefcdab89L);
+ ctx->m_nC = sal_uInt32(0x98badcfeL);
+ ctx->m_nD = sal_uInt32(0x10325476L);
+ ctx->m_nE = sal_uInt32(0xc3d2e1f0L);
+}
+
+static void updateSHA(DigestContextSHA *ctx)
+{
+ sal_uInt32 A, B, C, D, E, T;
+ sal_uInt32 *X;
+
+ DigestSHA_update_t *U;
+ U = ctx->m_update;
+
+ A = ctx->m_nA;
+ B = ctx->m_nB;
+ C = ctx->m_nC;
+ D = ctx->m_nD;
+ E = ctx->m_nE;
+ X = ctx->m_pData;
+
+ BODY_00_15 (U, 0, A, B, C, D, E, T);
+ BODY_00_15 (U, 1, T, A, B, C, D, E);
+ BODY_00_15 (U, 2, E, T, A, B, C, D);
+ BODY_00_15 (U, 3, D, E, T, A, B, C);
+ BODY_00_15 (U, 4, C, D, E, T, A, B);
+ BODY_00_15 (U, 5, B, C, D, E, T, A);
+ BODY_00_15 (U, 6, A, B, C, D, E, T);
+ BODY_00_15 (U, 7, T, A, B, C, D, E);
+ BODY_00_15 (U, 8, E, T, A, B, C, D);
+ BODY_00_15 (U, 9, D, E, T, A, B, C);
+ BODY_00_15 (U, 10, C, D, E, T, A, B);
+ BODY_00_15 (U, 11, B, C, D, E, T, A);
+ BODY_00_15 (U, 12, A, B, C, D, E, T);
+ BODY_00_15 (U, 13, T, A, B, C, D, E);
+ BODY_00_15 (U, 14, E, T, A, B, C, D);
+ BODY_00_15 (U, 15, D, E, T, A, B, C);
+ BODY_16_19 (U, 16, C, D, E, T, A, B);
+ BODY_16_19 (U, 17, B, C, D, E, T, A);
+ BODY_16_19 (U, 18, A, B, C, D, E, T);
+ BODY_16_19 (U, 19, T, A, B, C, D, E);
+
+ BODY_20_39 (U, 20, E, T, A, B, C, D);
+ BODY_20_39 (U, 21, D, E, T, A, B, C);
+ BODY_20_39 (U, 22, C, D, E, T, A, B);
+ BODY_20_39 (U, 23, B, C, D, E, T, A);
+ BODY_20_39 (U, 24, A, B, C, D, E, T);
+ BODY_20_39 (U, 25, T, A, B, C, D, E);
+ BODY_20_39 (U, 26, E, T, A, B, C, D);
+ BODY_20_39 (U, 27, D, E, T, A, B, C);
+ BODY_20_39 (U, 28, C, D, E, T, A, B);
+ BODY_20_39 (U, 29, B, C, D, E, T, A);
+ BODY_20_39 (U, 30, A, B, C, D, E, T);
+ BODY_20_39 (U, 31, T, A, B, C, D, E);
+ BODY_20_39 (U, 32, E, T, A, B, C, D);
+ BODY_20_39 (U, 33, D, E, T, A, B, C);
+ BODY_20_39 (U, 34, C, D, E, T, A, B);
+ BODY_20_39 (U, 35, B, C, D, E, T, A);
+ BODY_20_39 (U, 36, A, B, C, D, E, T);
+ BODY_20_39 (U, 37, T, A, B, C, D, E);
+ BODY_20_39 (U, 38, E, T, A, B, C, D);
+ BODY_20_39 (U, 39, D, E, T, A, B, C);
+
+ BODY_40_59 (U, 40, C, D, E, T, A, B);
+ BODY_40_59 (U, 41, B, C, D, E, T, A);
+ BODY_40_59 (U, 42, A, B, C, D, E, T);
+ BODY_40_59 (U, 43, T, A, B, C, D, E);
+ BODY_40_59 (U, 44, E, T, A, B, C, D);
+ BODY_40_59 (U, 45, D, E, T, A, B, C);
+ BODY_40_59 (U, 46, C, D, E, T, A, B);
+ BODY_40_59 (U, 47, B, C, D, E, T, A);
+ BODY_40_59 (U, 48, A, B, C, D, E, T);
+ BODY_40_59 (U, 49, T, A, B, C, D, E);
+ BODY_40_59 (U, 50, E, T, A, B, C, D);
+ BODY_40_59 (U, 51, D, E, T, A, B, C);
+ BODY_40_59 (U, 52, C, D, E, T, A, B);
+ BODY_40_59 (U, 53, B, C, D, E, T, A);
+ BODY_40_59 (U, 54, A, B, C, D, E, T);
+ BODY_40_59 (U, 55, T, A, B, C, D, E);
+ BODY_40_59 (U, 56, E, T, A, B, C, D);
+ BODY_40_59 (U, 57, D, E, T, A, B, C);
+ BODY_40_59 (U, 58, C, D, E, T, A, B);
+ BODY_40_59 (U, 59, B, C, D, E, T, A);
+
+ BODY_60_79 (U, 60, A, B, C, D, E, T);
+ BODY_60_79 (U, 61, T, A, B, C, D, E);
+ BODY_60_79 (U, 62, E, T, A, B, C, D);
+ BODY_60_79 (U, 63, D, E, T, A, B, C);
+ BODY_60_79 (U, 64, C, D, E, T, A, B);
+ BODY_60_79 (U, 65, B, C, D, E, T, A);
+ BODY_60_79 (U, 66, A, B, C, D, E, T);
+ BODY_60_79 (U, 67, T, A, B, C, D, E);
+ BODY_60_79 (U, 68, E, T, A, B, C, D);
+ BODY_60_79 (U, 69, D, E, T, A, B, C);
+ BODY_60_79 (U, 70, C, D, E, T, A, B);
+ BODY_60_79 (U, 71, B, C, D, E, T, A);
+ BODY_60_79 (U, 72, A, B, C, D, E, T);
+ BODY_60_79 (U, 73, T, A, B, C, D, E);
+ BODY_60_79 (U, 74, E, T, A, B, C, D);
+ BODY_60_79 (U, 75, D, E, T, A, B, C);
+ BODY_60_79 (U, 76, C, D, E, T, A, B);
+ BODY_60_79 (U, 77, B, C, D, E, T, A);
+ BODY_60_79 (U, 78, A, B, C, D, E, T);
+ BODY_60_79 (U, 79, T, A, B, C, D, E);
+
+ ctx->m_nA += E;
+ ctx->m_nB += T;
+ ctx->m_nC += A;
+ ctx->m_nD += B;
+ ctx->m_nE += C;
+}
+
+static void endSHA(DigestContextSHA *ctx)
+{
+ static const sal_uInt8 end[4] =
+ {
+ 0x80, 0x00, 0x00, 0x00
+ };
+ const sal_uInt8 *p = end;
+
+ sal_uInt32 *X;
+ int i;
+
+ X = ctx->m_pData;
+ i = (ctx->m_nDatLen >> 2);
+
+#ifdef OSL_BIGENDIAN
+ swapLong(X, i + 1);
+#endif /* OSL_BIGENDIAN */
+
+ switch (ctx->m_nDatLen & 0x03)
+ {
+ case 1: X[i] &= 0x000000ff; break;
+ case 2: X[i] &= 0x0000ffff; break;
+ case 3: X[i] &= 0x00ffffff; break;
+ }
+
+ switch (ctx->m_nDatLen & 0x03)
+ {
+ case 0: X[i] = static_cast<sal_uInt32>(*(p++)) << 0;
+ [[fallthrough]];
+ case 1: X[i] |= static_cast<sal_uInt32>(*(p++)) << 8;
+ [[fallthrough]];
+ case 2: X[i] |= static_cast<sal_uInt32>(*(p++)) << 16;
+ [[fallthrough]];
+ case 3: X[i] |= static_cast<sal_uInt32>(*(p++)) << 24;
+ }
+
+ swapLong(X, i + 1);
+
+ i += 1;
+
+ // tdf#114939 NB: this is WRONG and should be ">" not ">=" but is not
+ // fixed as this buggy SHA1 implementation is needed for compatibility
+ if (i >= (DIGEST_LBLOCK_SHA - 2))
+ {
+ for (; i < DIGEST_LBLOCK_SHA; i++)
+ {
+ X[i] = 0;
+ }
+
+ updateSHA(ctx);
+ i = 0;
+ }
+
+ for (; i < (DIGEST_LBLOCK_SHA - 2); i++)
+ {
+ X[i] = 0;
+ }
+
+ X[DIGEST_LBLOCK_SHA - 2] = ctx->m_nH;
+ X[DIGEST_LBLOCK_SHA - 1] = ctx->m_nL;
+
+ updateSHA(ctx);
+}
+
+const Digest_Impl SHA_0 =
+{
+ rtl_Digest_AlgorithmSHA,
+ RTL_DIGEST_LENGTH_SHA,
+ nullptr,
+ rtl_digest_destroySHA,
+ rtl_digest_updateSHA,
+ rtl_digest_getSHA
+};
+
+static sal_uInt32 updateSHA_0(sal_uInt32 x)
+{
+ return x;
+}
+
+rtlDigestError SAL_CALL rtl_digest_SHA(
+ const void *pData, sal_uInt32 nDatLen,
+ sal_uInt8 *pBuffer, sal_uInt32 nBufLen) SAL_THROW_EXTERN_C()
+{
+ DigestSHA_Impl digest;
+ rtlDigestError result;
+
+ digest.m_digest = SHA_0;
+ initSHA(&(digest.m_context), updateSHA_0);
+
+ result = rtl_digest_updateSHA(&digest, pData, nDatLen);
+ if (result == rtl_Digest_E_None)
+ result = rtl_digest_getSHA(&digest, pBuffer, nBufLen);
+
+ rtl_secureZeroMemory(&digest, sizeof(digest));
+ return result;
+}
+
+rtlDigest SAL_CALL rtl_digest_createSHA() SAL_THROW_EXTERN_C()
+{
+ DigestSHA_Impl *pImpl = RTL_DIGEST_CREATE(DigestSHA_Impl);
+ if (pImpl)
+ {
+ pImpl->m_digest = SHA_0;
+ initSHA(&(pImpl->m_context), updateSHA_0);
+ }
+ return static_cast<rtlDigest>(pImpl);
+}
+
+rtlDigestError SAL_CALL rtl_digest_updateSHA(
+ rtlDigest Digest, const void *pData, sal_uInt32 nDatLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestSHA_Impl *pImpl = static_cast<DigestSHA_Impl *>(Digest);
+ const sal_uInt8 *d = static_cast<const sal_uInt8 *>(pData);
+
+ DigestContextSHA *ctx;
+ sal_uInt32 len;
+
+ if (!pImpl || !pData)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmSHA)
+ return rtl_Digest_E_Algorithm;
+
+ if (nDatLen == 0)
+ return rtl_Digest_E_None;
+
+ ctx = &(pImpl->m_context);
+
+ len = ctx->m_nL + (nDatLen << 3);
+ if (len < ctx->m_nL)
+ ctx->m_nH += 1;
+
+ ctx->m_nH += (nDatLen >> 29);
+ ctx->m_nL = len;
+
+ if (ctx->m_nDatLen)
+ {
+ sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(ctx->m_pData) + ctx->m_nDatLen;
+ sal_uInt32 n = DIGEST_CBLOCK_SHA - ctx->m_nDatLen;
+
+ if (nDatLen < n)
+ {
+ memcpy(p, d, nDatLen);
+ ctx->m_nDatLen += nDatLen;
+
+ return rtl_Digest_E_None;
+ }
+
+ memcpy(p, d, n);
+ d += n;
+ nDatLen -= n;
+
+#ifndef OSL_BIGENDIAN
+ swapLong(ctx->m_pData, DIGEST_LBLOCK_SHA);
+#endif /* OSL_BIGENDIAN */
+
+ updateSHA(ctx);
+ ctx->m_nDatLen = 0;
+ }
+
+ while (nDatLen >= DIGEST_CBLOCK_SHA)
+ {
+ memcpy(ctx->m_pData, d, DIGEST_CBLOCK_SHA);
+ d += DIGEST_CBLOCK_SHA;
+ nDatLen -= DIGEST_CBLOCK_SHA;
+
+#ifndef OSL_BIGENDIAN
+ swapLong(ctx->m_pData, DIGEST_LBLOCK_SHA);
+#endif /* OSL_BIGENDIAN */
+
+ updateSHA(ctx);
+ }
+
+ memcpy(ctx->m_pData, d, nDatLen);
+ ctx->m_nDatLen = nDatLen;
+
+ return rtl_Digest_E_None;
+}
+
+rtlDigestError SAL_CALL rtl_digest_getSHA(
+ rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestSHA_Impl *pImpl = static_cast<DigestSHA_Impl *>(Digest);
+ sal_uInt8 *p = pBuffer;
+
+ DigestContextSHA *ctx;
+
+ if (!pImpl || !pBuffer)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmSHA)
+ return rtl_Digest_E_Algorithm;
+
+ if (pImpl->m_digest.m_length > nBufLen)
+ return rtl_Digest_E_BufferSize;
+
+ ctx = &(pImpl->m_context);
+
+ endSHA(ctx);
+ RTL_DIGEST_HTONL(ctx->m_nA, p);
+ RTL_DIGEST_HTONL(ctx->m_nB, p);
+ RTL_DIGEST_HTONL(ctx->m_nC, p);
+ RTL_DIGEST_HTONL(ctx->m_nD, p);
+ RTL_DIGEST_HTONL(ctx->m_nE, p);
+ initSHA(ctx, updateSHA_0);
+
+ return rtl_Digest_E_None;
+}
+
+void SAL_CALL rtl_digest_destroySHA(rtlDigest Digest) SAL_THROW_EXTERN_C()
+{
+ DigestSHA_Impl *pImpl = static_cast< DigestSHA_Impl * >(Digest);
+ if (pImpl)
+ {
+ if (pImpl->m_digest.m_algorithm == rtl_Digest_AlgorithmSHA)
+ rtl_freeZeroMemory(pImpl, sizeof(DigestSHA_Impl));
+ else
+ free(pImpl);
+ }
+}
+
+const Digest_Impl SHA_1 =
+{
+ rtl_Digest_AlgorithmSHA1,
+ RTL_DIGEST_LENGTH_SHA1,
+ nullptr,
+ rtl_digest_destroySHA1,
+ rtl_digest_updateSHA1,
+ rtl_digest_getSHA1
+};
+
+static sal_uInt32 updateSHA_1(sal_uInt32 x)
+{
+ return RTL_DIGEST_ROTL(x, 1);
+}
+
+rtlDigestError SAL_CALL rtl_digest_SHA1(
+ const void *pData, sal_uInt32 nDatLen,
+ sal_uInt8 *pBuffer, sal_uInt32 nBufLen) SAL_THROW_EXTERN_C()
+{
+ DigestSHA_Impl digest;
+ rtlDigestError result;
+
+ digest.m_digest = SHA_1;
+ initSHA(&(digest.m_context), updateSHA_1);
+
+ result = rtl_digest_updateSHA1(&digest, pData, nDatLen);
+ if (result == rtl_Digest_E_None)
+ result = rtl_digest_getSHA1(&digest, pBuffer, nBufLen);
+
+ rtl_secureZeroMemory(&digest, sizeof(digest));
+ return result;
+}
+
+rtlDigest SAL_CALL rtl_digest_createSHA1() SAL_THROW_EXTERN_C()
+{
+ DigestSHA_Impl *pImpl = RTL_DIGEST_CREATE(DigestSHA_Impl);
+ if (pImpl)
+ {
+ pImpl->m_digest = SHA_1;
+ initSHA(&(pImpl->m_context), updateSHA_1);
+ }
+ return static_cast<rtlDigest>(pImpl);
+}
+
+rtlDigestError SAL_CALL rtl_digest_updateSHA1(
+ rtlDigest Digest, const void *pData, sal_uInt32 nDatLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestSHA_Impl *pImpl = static_cast< DigestSHA_Impl * >(Digest);
+ const sal_uInt8 *d = static_cast< const sal_uInt8 * >(pData);
+
+ DigestContextSHA *ctx;
+ sal_uInt32 len;
+
+ if (!pImpl || !pData)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmSHA1)
+ return rtl_Digest_E_Algorithm;
+
+ if (nDatLen == 0)
+ return rtl_Digest_E_None;
+
+ ctx = &(pImpl->m_context);
+
+ len = ctx->m_nL + (nDatLen << 3);
+ if (len < ctx->m_nL)
+ ctx->m_nH += 1;
+
+ ctx->m_nH += (nDatLen >> 29);
+ ctx->m_nL = len;
+
+ if (ctx->m_nDatLen)
+ {
+ sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(ctx->m_pData) + ctx->m_nDatLen;
+ sal_uInt32 n = DIGEST_CBLOCK_SHA - ctx->m_nDatLen;
+
+ if (nDatLen < n)
+ {
+ memcpy(p, d, nDatLen);
+ ctx->m_nDatLen += nDatLen;
+
+ return rtl_Digest_E_None;
+ }
+
+ memcpy(p, d, n);
+ d += n;
+ nDatLen -= n;
+
+#ifndef OSL_BIGENDIAN
+ swapLong(ctx->m_pData, DIGEST_LBLOCK_SHA);
+#endif /* OSL_BIGENDIAN */
+
+ updateSHA(ctx);
+ ctx->m_nDatLen = 0;
+ }
+
+ while (nDatLen >= DIGEST_CBLOCK_SHA)
+ {
+ memcpy(ctx->m_pData, d, DIGEST_CBLOCK_SHA);
+ d += DIGEST_CBLOCK_SHA;
+ nDatLen -= DIGEST_CBLOCK_SHA;
+
+#ifndef OSL_BIGENDIAN
+ swapLong(ctx->m_pData, DIGEST_LBLOCK_SHA);
+#endif /* OSL_BIGENDIAN */
+
+ updateSHA(ctx);
+ }
+
+ memcpy(ctx->m_pData, d, nDatLen);
+ ctx->m_nDatLen = nDatLen;
+
+ return rtl_Digest_E_None;
+}
+
+rtlDigestError SAL_CALL rtl_digest_getSHA1 (
+ rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestSHA_Impl *pImpl = static_cast<DigestSHA_Impl *>(Digest);
+ sal_uInt8 *p = pBuffer;
+
+ DigestContextSHA *ctx;
+
+ if (!pImpl || !pBuffer)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmSHA1)
+ return rtl_Digest_E_Algorithm;
+
+ if (pImpl->m_digest.m_length > nBufLen)
+ return rtl_Digest_E_BufferSize;
+
+ ctx = &(pImpl->m_context);
+
+ endSHA(ctx);
+ RTL_DIGEST_HTONL(ctx->m_nA, p);
+ RTL_DIGEST_HTONL(ctx->m_nB, p);
+ RTL_DIGEST_HTONL(ctx->m_nC, p);
+ RTL_DIGEST_HTONL(ctx->m_nD, p);
+ RTL_DIGEST_HTONL(ctx->m_nE, p);
+ initSHA(ctx, updateSHA_1);
+
+ return rtl_Digest_E_None;
+}
+
+void SAL_CALL rtl_digest_destroySHA1(rtlDigest Digest) SAL_THROW_EXTERN_C()
+{
+ DigestSHA_Impl *pImpl = static_cast< DigestSHA_Impl * >(Digest);
+ if (pImpl)
+ {
+ if (pImpl->m_digest.m_algorithm == rtl_Digest_AlgorithmSHA1)
+ rtl_freeZeroMemory(pImpl, sizeof(DigestSHA_Impl));
+ else
+ free(pImpl);
+ }
+}
+
+#define DIGEST_CBLOCK_HMAC_MD5 64
+
+namespace {
+
+struct ContextHMAC_MD5
+{
+ DigestMD5_Impl m_hash;
+ sal_uInt8 m_opad[DIGEST_CBLOCK_HMAC_MD5];
+};
+
+struct DigestHMAC_MD5_Impl
+{
+ Digest_Impl m_digest;
+ ContextHMAC_MD5 m_context;
+};
+
+}
+
+static void initHMAC_MD5(ContextHMAC_MD5 * ctx);
+static void ipadHMAC_MD5(ContextHMAC_MD5 * ctx);
+static void opadHMAC_MD5(ContextHMAC_MD5 * ctx);
+
+const Digest_Impl HMAC_MD5 =
+{
+ rtl_Digest_AlgorithmHMAC_MD5,
+ RTL_DIGEST_LENGTH_MD5,
+
+ rtl_digest_initHMAC_MD5,
+ rtl_digest_destroyHMAC_MD5,
+ rtl_digest_updateHMAC_MD5,
+ rtl_digest_getHMAC_MD5
+};
+
+static void initHMAC_MD5(ContextHMAC_MD5 * ctx)
+{
+ DigestMD5_Impl *pImpl = &(ctx->m_hash);
+
+ pImpl->m_digest = MD5;
+ initMD5(&(pImpl->m_context));
+
+ memset(ctx->m_opad, 0, DIGEST_CBLOCK_HMAC_MD5);
+}
+
+static void ipadHMAC_MD5(ContextHMAC_MD5 * ctx)
+{
+ sal_uInt32 i;
+
+ for (i = 0; i < DIGEST_CBLOCK_HMAC_MD5; i++)
+ {
+ ctx->m_opad[i] ^= 0x36;
+ }
+
+ rtl_digest_updateMD5(&(ctx->m_hash), ctx->m_opad, DIGEST_CBLOCK_HMAC_MD5);
+
+ for (i = 0; i < DIGEST_CBLOCK_HMAC_MD5; i++)
+ {
+ ctx->m_opad[i] ^= 0x36;
+ }
+}
+
+static void opadHMAC_MD5(ContextHMAC_MD5 * ctx)
+{
+ sal_uInt32 i;
+
+ for (i = 0; i < DIGEST_CBLOCK_HMAC_MD5; i++)
+ {
+ ctx->m_opad[i] ^= 0x5c;
+ }
+}
+
+rtlDigestError SAL_CALL rtl_digest_HMAC_MD5(
+ const sal_uInt8 *pKeyData, sal_uInt32 nKeyLen,
+ const void *pData, sal_uInt32 nDatLen,
+ sal_uInt8 *pBuffer, sal_uInt32 nBufLen) SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_MD5_Impl digest;
+ rtlDigestError result;
+
+ digest.m_digest = HMAC_MD5;
+
+ result = rtl_digest_initHMAC_MD5(&digest, pKeyData, nKeyLen);
+ if (result == rtl_Digest_E_None)
+ {
+ result = rtl_digest_updateHMAC_MD5(&digest, pData, nDatLen);
+ if (result == rtl_Digest_E_None)
+ result = rtl_digest_getHMAC_MD5(&digest, pBuffer, nBufLen);
+ }
+
+ rtl_secureZeroMemory(&digest, sizeof(digest));
+ return result;
+}
+
+rtlDigest SAL_CALL rtl_digest_createHMAC_MD5() SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_MD5_Impl *pImpl = RTL_DIGEST_CREATE(DigestHMAC_MD5_Impl);
+ if (pImpl)
+ {
+ pImpl->m_digest = HMAC_MD5;
+ initHMAC_MD5(&(pImpl->m_context));
+ }
+ return static_cast< rtlDigest >(pImpl);
+}
+
+rtlDigestError SAL_CALL rtl_digest_initHMAC_MD5(
+ rtlDigest Digest, const sal_uInt8 *pKeyData, sal_uInt32 nKeyLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_MD5_Impl *pImpl = static_cast< DigestHMAC_MD5_Impl* >(Digest);
+ ContextHMAC_MD5 *ctx;
+
+ if (!pImpl || !pKeyData)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmHMAC_MD5)
+ return rtl_Digest_E_Algorithm;
+
+ ctx = &(pImpl->m_context);
+ initHMAC_MD5(ctx);
+
+ if (nKeyLen > DIGEST_CBLOCK_HMAC_MD5)
+ {
+ /* Initialize 'opad' with hashed 'KeyData' */
+ rtl_digest_updateMD5(&(ctx->m_hash), pKeyData, nKeyLen);
+ rtl_digest_getMD5(&(ctx->m_hash), ctx->m_opad, RTL_DIGEST_LENGTH_MD5);
+ }
+ else
+ {
+ /* Initialize 'opad' with plain 'KeyData' */
+ memcpy(ctx->m_opad, pKeyData, nKeyLen);
+ }
+
+ ipadHMAC_MD5(ctx);
+ opadHMAC_MD5(ctx);
+
+ return rtl_Digest_E_None;
+}
+
+rtlDigestError SAL_CALL rtl_digest_updateHMAC_MD5(
+ rtlDigest Digest, const void *pData, sal_uInt32 nDatLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_MD5_Impl *pImpl = static_cast< DigestHMAC_MD5_Impl* >(Digest);
+ ContextHMAC_MD5 *ctx;
+
+ if (!pImpl || !pData)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmHMAC_MD5)
+ return rtl_Digest_E_Algorithm;
+
+ ctx = &(pImpl->m_context);
+ rtl_digest_updateMD5(&(ctx->m_hash), pData, nDatLen);
+
+ return rtl_Digest_E_None;
+}
+
+rtlDigestError SAL_CALL rtl_digest_getHMAC_MD5(
+ rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_MD5_Impl *pImpl = static_cast<DigestHMAC_MD5_Impl*>(Digest);
+ ContextHMAC_MD5 *ctx;
+
+ if (!pImpl || !pBuffer)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmHMAC_MD5)
+ return rtl_Digest_E_Algorithm;
+
+ if (pImpl->m_digest.m_length > nBufLen)
+ return rtl_Digest_E_BufferSize;
+
+ nBufLen = pImpl->m_digest.m_length;
+
+ ctx = &(pImpl->m_context);
+ rtl_digest_getMD5(&(ctx->m_hash), pBuffer, nBufLen);
+
+ rtl_digest_updateMD5(&(ctx->m_hash), ctx->m_opad, 64);
+ rtl_digest_updateMD5(&(ctx->m_hash), pBuffer, nBufLen);
+ rtl_digest_getMD5(&(ctx->m_hash), pBuffer, nBufLen);
+
+ opadHMAC_MD5(ctx);
+ ipadHMAC_MD5(ctx);
+ opadHMAC_MD5(ctx);
+
+ return rtl_Digest_E_None;
+}
+
+void SAL_CALL rtl_digest_destroyHMAC_MD5(rtlDigest Digest) SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_MD5_Impl *pImpl = static_cast< DigestHMAC_MD5_Impl* >(Digest);
+ if (pImpl)
+ {
+ if (pImpl->m_digest.m_algorithm == rtl_Digest_AlgorithmHMAC_MD5)
+ rtl_freeZeroMemory(pImpl, sizeof(DigestHMAC_MD5_Impl));
+ else
+ free(pImpl);
+ }
+}
+
+#define DIGEST_CBLOCK_HMAC_SHA1 64
+
+namespace {
+
+struct ContextHMAC_SHA1
+{
+ DigestSHA_Impl m_hash;
+ sal_uInt8 m_opad[DIGEST_CBLOCK_HMAC_SHA1];
+};
+
+struct DigestHMAC_SHA1_Impl
+{
+ Digest_Impl m_digest;
+ ContextHMAC_SHA1 m_context;
+};
+
+}
+
+static void initHMAC_SHA1(ContextHMAC_SHA1 * ctx);
+static void ipadHMAC_SHA1(ContextHMAC_SHA1 * ctx);
+static void opadHMAC_SHA1(ContextHMAC_SHA1 * ctx);
+
+const Digest_Impl HMAC_SHA1 =
+{
+ rtl_Digest_AlgorithmHMAC_SHA1,
+ RTL_DIGEST_LENGTH_SHA1,
+ rtl_digest_initHMAC_SHA1,
+ rtl_digest_destroyHMAC_SHA1,
+ rtl_digest_updateHMAC_SHA1,
+ rtl_digest_getHMAC_SHA1
+};
+
+static void initHMAC_SHA1(ContextHMAC_SHA1 * ctx)
+{
+ DigestSHA_Impl *pImpl = &(ctx->m_hash);
+
+ pImpl->m_digest = SHA_1;
+ initSHA(&(pImpl->m_context), updateSHA_1);
+
+ memset(ctx->m_opad, 0, DIGEST_CBLOCK_HMAC_SHA1);
+}
+
+static void ipadHMAC_SHA1(ContextHMAC_SHA1 * ctx)
+{
+ sal_uInt32 i;
+
+ for (i = 0; i < DIGEST_CBLOCK_HMAC_SHA1; i++)
+ {
+ ctx->m_opad[i] ^= 0x36;
+ }
+
+ rtl_digest_updateSHA1(&(ctx->m_hash), ctx->m_opad, DIGEST_CBLOCK_HMAC_SHA1);
+
+ for (i = 0; i < DIGEST_CBLOCK_HMAC_SHA1; i++)
+ {
+ ctx->m_opad[i] ^= 0x36;
+ }
+}
+
+static void opadHMAC_SHA1(ContextHMAC_SHA1 * ctx)
+{
+ sal_uInt32 i;
+
+ for (i = 0; i < DIGEST_CBLOCK_HMAC_SHA1; i++)
+ {
+ ctx->m_opad[i] ^= 0x5c;
+ }
+}
+
+rtlDigestError SAL_CALL rtl_digest_HMAC_SHA1(
+ const sal_uInt8 *pKeyData, sal_uInt32 nKeyLen,
+ const void *pData, sal_uInt32 nDatLen,
+ sal_uInt8 *pBuffer, sal_uInt32 nBufLen) SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_SHA1_Impl digest;
+ rtlDigestError result;
+
+ digest.m_digest = HMAC_SHA1;
+
+ result = rtl_digest_initHMAC_SHA1(&digest, pKeyData, nKeyLen);
+ if (result == rtl_Digest_E_None)
+ {
+ result = rtl_digest_updateHMAC_SHA1(&digest, pData, nDatLen);
+ if (result == rtl_Digest_E_None)
+ result = rtl_digest_getHMAC_SHA1(&digest, pBuffer, nBufLen);
+ }
+
+ rtl_secureZeroMemory(&digest, sizeof(digest));
+ return result;
+}
+
+rtlDigest SAL_CALL rtl_digest_createHMAC_SHA1() SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_SHA1_Impl *pImpl = RTL_DIGEST_CREATE(DigestHMAC_SHA1_Impl);
+ if (pImpl)
+ {
+ pImpl->m_digest = HMAC_SHA1;
+ initHMAC_SHA1(&(pImpl->m_context));
+ }
+ return static_cast<rtlDigest>(pImpl);
+}
+
+rtlDigestError SAL_CALL rtl_digest_initHMAC_SHA1(
+ rtlDigest Digest, const sal_uInt8 *pKeyData, sal_uInt32 nKeyLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_SHA1_Impl *pImpl = static_cast<DigestHMAC_SHA1_Impl*>(Digest);
+ ContextHMAC_SHA1 *ctx;
+
+ if (!pImpl || !pKeyData)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmHMAC_SHA1)
+ return rtl_Digest_E_Algorithm;
+
+ ctx = &(pImpl->m_context);
+ initHMAC_SHA1(ctx);
+
+ if (nKeyLen > DIGEST_CBLOCK_HMAC_SHA1)
+ {
+ /* Initialize 'opad' with hashed 'KeyData' */
+ rtl_digest_updateSHA1(&(ctx->m_hash), pKeyData, nKeyLen);
+ rtl_digest_getSHA1(&(ctx->m_hash), ctx->m_opad, RTL_DIGEST_LENGTH_SHA1);
+ }
+ else
+ {
+ /* Initialize 'opad' with plain 'KeyData' */
+ memcpy(ctx->m_opad, pKeyData, nKeyLen);
+ }
+
+ ipadHMAC_SHA1(ctx);
+ opadHMAC_SHA1(ctx);
+
+ return rtl_Digest_E_None;
+}
+
+rtlDigestError SAL_CALL rtl_digest_updateHMAC_SHA1(
+ rtlDigest Digest, const void *pData, sal_uInt32 nDatLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_SHA1_Impl *pImpl = static_cast<DigestHMAC_SHA1_Impl*>(Digest);
+ ContextHMAC_SHA1 *ctx;
+
+ if (!pImpl || !pData)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmHMAC_SHA1)
+ return rtl_Digest_E_Algorithm;
+
+ ctx = &(pImpl->m_context);
+ rtl_digest_updateSHA1(&(ctx->m_hash), pData, nDatLen);
+
+ return rtl_Digest_E_None;
+}
+
+rtlDigestError SAL_CALL rtl_digest_getHMAC_SHA1(
+ rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen)
+ SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_SHA1_Impl *pImpl = static_cast<DigestHMAC_SHA1_Impl*>(Digest);
+ ContextHMAC_SHA1 *ctx;
+
+ if (!pImpl || !pBuffer)
+ return rtl_Digest_E_Argument;
+
+ if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmHMAC_SHA1)
+ return rtl_Digest_E_Algorithm;
+
+ if (pImpl->m_digest.m_length > nBufLen)
+ return rtl_Digest_E_BufferSize;
+
+ nBufLen = pImpl->m_digest.m_length;
+
+ ctx = &(pImpl->m_context);
+ rtl_digest_getSHA1(&(ctx->m_hash), pBuffer, nBufLen);
+
+ rtl_digest_updateSHA1(&(ctx->m_hash), ctx->m_opad, sizeof(ctx->m_opad));
+ rtl_digest_updateSHA1(&(ctx->m_hash), pBuffer, nBufLen);
+ rtl_digest_getSHA1(&(ctx->m_hash), pBuffer, nBufLen);
+
+ opadHMAC_SHA1(ctx);
+ ipadHMAC_SHA1(ctx);
+ opadHMAC_SHA1(ctx);
+
+ return rtl_Digest_E_None;
+}
+
+void SAL_CALL rtl_digest_destroyHMAC_SHA1(rtlDigest Digest)
+ SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_SHA1_Impl *pImpl = static_cast<DigestHMAC_SHA1_Impl*>(Digest);
+ if (pImpl)
+ {
+ if (pImpl->m_digest.m_algorithm == rtl_Digest_AlgorithmHMAC_SHA1)
+ rtl_freeZeroMemory(pImpl, sizeof(DigestHMAC_SHA1_Impl));
+ else
+ free(pImpl);
+ }
+}
+
+#define DIGEST_CBLOCK_PBKDF2 RTL_DIGEST_LENGTH_HMAC_SHA1
+
+static void updatePBKDF2(
+ rtlDigest hDigest,
+ sal_uInt8 T[DIGEST_CBLOCK_PBKDF2],
+ const sal_uInt8 *pSaltData, sal_uInt32 nSaltLen,
+ sal_uInt32 nCount, sal_uInt32 nIndex)
+{
+ /* T_i = F (P, S, c, i) */
+ sal_uInt8 U[DIGEST_CBLOCK_PBKDF2];
+ sal_uInt32 i, k;
+
+ /* U_(1) = PRF (P, S || INDEX) */
+ rtl_digest_updateHMAC_SHA1(hDigest, pSaltData, nSaltLen);
+ rtl_digest_updateHMAC_SHA1(hDigest, &nIndex, sizeof(nIndex));
+ rtl_digest_getHMAC_SHA1(hDigest, U, DIGEST_CBLOCK_PBKDF2);
+
+ /* T = U_(1) */
+ for (k = 0; k < DIGEST_CBLOCK_PBKDF2; k++)
+ {
+ T[k] = U[k];
+ }
+
+ /* T ^= U_(2) ^ ... ^ U_(c) */
+ for (i = 1; i < nCount; i++)
+ {
+ /* U_(i) = PRF (P, U_(i-1)) */
+ rtl_digest_updateHMAC_SHA1(hDigest, U, DIGEST_CBLOCK_PBKDF2);
+ rtl_digest_getHMAC_SHA1(hDigest, U, DIGEST_CBLOCK_PBKDF2);
+
+ /* T ^= U_(i) */
+ for (k = 0; k < DIGEST_CBLOCK_PBKDF2; k++)
+ {
+ T[k] ^= U[k];
+ }
+ }
+
+ rtl_secureZeroMemory(U, DIGEST_CBLOCK_PBKDF2);
+}
+
+rtlDigestError SAL_CALL rtl_digest_PBKDF2(
+ sal_uInt8 *pKeyData , sal_uInt32 nKeyLen,
+ const sal_uInt8 *pPassData, sal_uInt32 nPassLen,
+ const sal_uInt8 *pSaltData, sal_uInt32 nSaltLen,
+ sal_uInt32 nCount) SAL_THROW_EXTERN_C()
+{
+ DigestHMAC_SHA1_Impl digest;
+ sal_uInt32 i = 1;
+
+ if (!pKeyData || !pPassData || !pSaltData)
+ return rtl_Digest_E_Argument;
+
+ digest.m_digest = HMAC_SHA1;
+ rtl_digest_initHMAC_SHA1(&digest, pPassData, nPassLen);
+
+ /* DK = T_(1) || T_(2) || ... || T_(l) */
+ while (nKeyLen >= DIGEST_CBLOCK_PBKDF2)
+ {
+ /* T_(i) = F (P, S, c, i); DK ||= T_(i) */
+ updatePBKDF2(
+ &digest, pKeyData,
+ pSaltData, nSaltLen,
+ nCount, OSL_NETDWORD(i));
+
+ /* Next 'KeyData' block */
+ pKeyData += DIGEST_CBLOCK_PBKDF2;
+ nKeyLen -= DIGEST_CBLOCK_PBKDF2;
+ i += 1;
+ }
+
+ if (nKeyLen > 0)
+ {
+ /* Last 'KeyData' block */
+ sal_uInt8 T[DIGEST_CBLOCK_PBKDF2];
+
+ /* T_i = F (P, S, c, i) */
+ updatePBKDF2(
+ &digest, T,
+ pSaltData, nSaltLen,
+ nCount, OSL_NETDWORD(i));
+
+ /* DK ||= T_(i) */
+ memcpy(pKeyData, T, nKeyLen);
+ rtl_secureZeroMemory(T, DIGEST_CBLOCK_PBKDF2);
+ }
+
+ rtl_secureZeroMemory(&digest, sizeof(digest));
+ return rtl_Digest_E_None;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/hash.cxx b/sal/rtl/hash.cxx
new file mode 100644
index 000000000..4fed60889
--- /dev/null
+++ b/sal/rtl/hash.cxx
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <stdlib.h>
+
+#include "hash.hxx"
+#include "strimp.hxx"
+#include <osl/diagnose.h>
+#include <sal/macros.h>
+
+namespace {
+
+struct StringHashTableImpl {
+ sal_uInt32 nEntries;
+ sal_uInt32 nSize;
+ rtl_uString **pData;
+};
+
+}
+
+typedef StringHashTableImpl StringHashTable;
+
+// Only for use in the implementation
+static StringHashTable *rtl_str_hash_new(sal_uInt32 nSize);
+static void rtl_str_hash_free(StringHashTable *pHash);
+
+static StringHashTable * getHashTable()
+{
+ static StringHashTable* pInternPool = rtl_str_hash_new(1024);
+ return pInternPool;
+}
+
+// Better / smaller / faster hash set...
+
+// TODO: add bottom bit-set list terminator to string list
+
+static sal_uInt32 getNextSize(sal_uInt32 nSize)
+{
+ // Sedgewick - Algorithms in C P577.
+ static const sal_uInt32 nPrimes[] = { 1021, 2039, 4093, 8191, 16381, 32749,
+ 65521, 131071,262139, 524287, 1048573,
+ 2097143, 4194301, 8388593, 16777213,
+ 33554393, 67108859, 134217689 };
+
+ for (sal_uInt32 nPrime : nPrimes)
+ {
+ if (nPrime > nSize)
+ return nPrime;
+ }
+ return nSize * 2;
+}
+
+static sal_uInt32 hashString(rtl_uString *pString)
+{
+ return static_cast<sal_uInt32>(rtl_ustr_hashCode_WithLength(pString->buffer,
+ pString->length));
+}
+
+static StringHashTable * rtl_str_hash_new(sal_uInt32 nSize)
+{
+ StringHashTable *pHash = static_cast<StringHashTable *>(malloc(sizeof(StringHashTable)));
+
+ pHash->nEntries = 0;
+ pHash->nSize = getNextSize (nSize);
+ pHash->pData = static_cast< rtl_uString ** >(calloc(sizeof(rtl_uString *), pHash->nSize));
+
+ return pHash;
+}
+
+static void rtl_str_hash_free(StringHashTable *pHash)
+{
+ if (!pHash)
+ return;
+
+ if (pHash->pData)
+ free(pHash->pData);
+
+ free(pHash);
+}
+
+static void
+rtl_str_hash_insert_nonequal(StringHashTable *pHash,
+ rtl_uString *pString)
+{
+ sal_uInt32 nHash = hashString(pString);
+ sal_uInt32 n;
+
+ n = nHash % pHash->nSize;
+ while (pHash->pData[n])
+ {
+ n++;
+ if (n >= pHash->nSize)
+ n = 0;
+ }
+ pHash->pData[n] = pString;
+}
+
+static void rtl_str_hash_resize(sal_uInt32 nNewSize)
+{
+ sal_uInt32 i;
+ StringHashTable *pNewHash;
+ StringHashTable *pHash = getHashTable();
+
+ OSL_ASSERT(nNewSize > pHash->nEntries);
+
+ pNewHash = rtl_str_hash_new(nNewSize);
+
+ for (i = 0; i < pHash->nSize; i++)
+ {
+ if (pHash->pData[i])
+ rtl_str_hash_insert_nonequal(pNewHash, pHash->pData[i]);
+ }
+
+ pNewHash->nEntries = pHash->nEntries;
+ free(pHash->pData);
+ *pHash = *pNewHash;
+ pNewHash->pData = nullptr;
+ rtl_str_hash_free(pNewHash);
+}
+
+static bool compareEqual(rtl_uString *pStringA, rtl_uString *pStringB)
+{
+ if (pStringA == pStringB)
+ return true;
+
+ if (pStringA->length != pStringB->length)
+ return false;
+
+ return !rtl_ustr_compare_WithLength( pStringA->buffer, pStringA->length,
+ pStringB->buffer, pStringB->length);
+}
+
+rtl_uString * rtl_str_hash_intern (
+ rtl_uString *pString,
+ int can_return)
+{
+ sal_uInt32 nHash = hashString(pString);
+ sal_uInt32 n;
+ rtl_uString *pHashStr;
+
+ StringHashTable *pHash = getHashTable();
+
+ // Should we resize ?
+ if (pHash->nEntries >= pHash->nSize/2)
+ rtl_str_hash_resize(getNextSize(pHash->nSize));
+
+ n = nHash % pHash->nSize;
+ while ((pHashStr = pHash->pData[n]))
+ {
+ if (compareEqual(pHashStr, pString))
+ {
+ rtl_uString_acquire(pHashStr);
+ return pHashStr;
+ }
+
+ n++;
+ if (n >= pHash->nSize)
+ n = 0;
+ }
+
+ if (!can_return)
+ {
+ rtl_uString *pCopy = nullptr;
+ rtl_uString_newFromString( &pCopy, pString );
+ pString = pCopy;
+
+ if (!pString)
+ return nullptr;
+ }
+
+ if (!SAL_STRING_IS_STATIC(pString))
+ pString->refCount |= SAL_STRING_INTERN_FLAG;
+
+ pHash->pData[n] = pString;
+ pHash->nEntries++;
+
+ return pString;
+}
+
+void rtl_str_hash_remove(rtl_uString *pString)
+{
+ sal_uInt32 n;
+ sal_uInt32 nHash = hashString(pString);
+ rtl_uString *pHashStr;
+
+ StringHashTable *pHash = getHashTable();
+
+ n = nHash % pHash->nSize;
+ while ((pHashStr = pHash->pData[n]))
+ {
+ if (compareEqual(pHashStr, pString))
+ break;
+
+ n++;
+
+ if (n >= pHash->nSize)
+ n = 0;
+ }
+
+ OSL_ASSERT(pHash->pData[n]);
+ if (!pHash->pData[n])
+ return;
+
+ pHash->pData[n++] = nullptr;
+ pHash->nEntries--;
+
+ if (n >= pHash->nSize)
+ n = 0;
+
+ while ((pHashStr = pHash->pData[n]))
+ {
+ pHash->pData[n] = nullptr;
+ // FIXME: rather unsophisticated and N^2 in chain-length, but robust.
+ rtl_str_hash_insert_nonequal(pHash, pHashStr);
+ n++;
+
+ if (n >= pHash->nSize)
+ n = 0;
+ }
+ // FIXME: Should we down-size ?
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/hash.hxx b/sal/rtl/hash.hxx
new file mode 100644
index 000000000..9f80448e7
--- /dev/null
+++ b/sal/rtl/hash.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_RTL_HASH_HXX
+#define INCLUDED_SAL_RTL_HASH_HXX
+
+#include <rtl/ustring.h>
+
+/* These functions are not multi-thread safe: */
+
+rtl_uString *rtl_str_hash_intern (rtl_uString *pString,
+ int can_return);
+void rtl_str_hash_remove (rtl_uString *pString);
+
+#endif // INCLUDED_SAL_RTL_HASH_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/locale.cxx b/sal/rtl/locale.cxx
new file mode 100644
index 000000000..742041ac1
--- /dev/null
+++ b/sal/rtl/locale.cxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdlib.h>
+#include <rtl/locale.h>
+
+#include <osl/diagnose.h>
+
+#include <rtllifecycle.h>
+#include <memory>
+#include <unordered_map>
+
+namespace {
+
+struct locale_deleter
+{
+ void operator() (rtl_Locale* p) noexcept
+ {
+ rtl_uString_release(p->Language);
+ rtl_uString_release(p->Country);
+ rtl_uString_release(p->Variant);
+ delete p;
+ }
+};
+
+}
+
+using locale_unique_ptr = std::unique_ptr<rtl_Locale, locale_deleter>;
+
+static std::unordered_map<sal_Int32, locale_unique_ptr> g_aLocaleTable;
+
+static rtl_Locale* g_pDefaultLocale = nullptr;
+
+void rtl_locale_init()
+{
+}
+
+void rtl_locale_fini()
+{
+ g_aLocaleTable.clear();
+ g_pDefaultLocale = nullptr;
+}
+
+rtl_Locale * SAL_CALL rtl_locale_register(const sal_Unicode * language, const sal_Unicode * country, const sal_Unicode * variant)
+{
+ sal_Unicode c = 0;
+ rtl_uString* sLanguage = nullptr;
+ rtl_uString* sCountry = nullptr;
+ rtl_uString* sVariant = nullptr;
+ sal_Int32 hashCode = -1;
+
+ if (!country)
+ country = &c;
+
+ if (!variant)
+ variant = &c;
+
+ ensureLocaleSingleton();
+
+ hashCode = rtl_ustr_hashCode(language) ^ rtl_ustr_hashCode(country) ^ rtl_ustr_hashCode(variant);
+
+ auto it = g_aLocaleTable.find(hashCode);
+ if (it != g_aLocaleTable.end())
+ return it->second.get();
+
+ rtl_uString_newFromStr(&sLanguage, language);
+ rtl_uString_newFromStr(&sCountry, country);
+ rtl_uString_newFromStr(&sVariant, variant);
+
+ locale_unique_ptr newLocale(new rtl_Locale);
+
+ newLocale->Language = sLanguage;
+ newLocale->Country = sCountry;
+ newLocale->Variant = sVariant;
+ newLocale->HashCode = hashCode;
+
+ auto ret = newLocale.get();
+ g_aLocaleTable.insert(it, std::pair<sal_Int32, locale_unique_ptr>( hashCode, std::move(newLocale) ) );
+ return ret;
+}
+
+rtl_Locale * SAL_CALL rtl_locale_getDefault()
+{
+ return g_pDefaultLocale;
+}
+
+void SAL_CALL rtl_locale_setDefault(const sal_Unicode * language, const sal_Unicode * country, const sal_Unicode * variant)
+{
+ g_pDefaultLocale = rtl_locale_register(language, country, variant);
+}
+
+rtl_uString * SAL_CALL rtl_locale_getLanguage(rtl_Locale * This)
+{
+ rtl_uString_acquire(This->Language);
+ return This->Language;
+}
+
+rtl_uString * SAL_CALL rtl_locale_getCountry(rtl_Locale * This)
+{
+ rtl_uString_acquire(This->Country);
+ return This->Country;
+}
+
+rtl_uString * SAL_CALL rtl_locale_getVariant(rtl_Locale * This)
+{
+ rtl_uString_acquire(This->Variant);
+ return This->Variant;
+}
+
+sal_Int32 SAL_CALL rtl_locale_hashCode(rtl_Locale * This)
+{
+ return This->HashCode;
+}
+
+sal_Int32 SAL_CALL rtl_locale_equals(rtl_Locale * This, rtl_Locale * obj)
+{
+ return This == obj;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/math.cxx b/sal/rtl/math.cxx
new file mode 100644
index 000000000..0d70e9718
--- /dev/null
+++ b/sal/rtl/math.cxx
@@ -0,0 +1,828 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/math.h>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <rtl/character.hxx>
+#include <rtl/math.hxx>
+
+#include <algorithm>
+#include <cassert>
+#include <cfenv>
+#include <cmath>
+#include <float.h>
+#include <limits>
+#include <limits.h>
+#include <math.h>
+#include <memory>
+#include <stdlib.h>
+
+#include "strtmpl.hxx"
+
+#include <dtoa.h>
+
+constexpr int minExp = -323, maxExp = 308;
+constexpr double n10s[] = {
+ 1e-323, 1e-322, 1e-321, 1e-320, 1e-319, 1e-318, 1e-317, 1e-316, 1e-315, 1e-314, 1e-313, 1e-312,
+ 1e-311, 1e-310, 1e-309, 1e-308, 1e-307, 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300,
+ 1e-299, 1e-298, 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290, 1e-289, 1e-288,
+ 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281, 1e-280, 1e-279, 1e-278, 1e-277, 1e-276,
+ 1e-275, 1e-274, 1e-273, 1e-272, 1e-271, 1e-270, 1e-269, 1e-268, 1e-267, 1e-266, 1e-265, 1e-264,
+ 1e-263, 1e-262, 1e-261, 1e-260, 1e-259, 1e-258, 1e-257, 1e-256, 1e-255, 1e-254, 1e-253, 1e-252,
+ 1e-251, 1e-250, 1e-249, 1e-248, 1e-247, 1e-246, 1e-245, 1e-244, 1e-243, 1e-242, 1e-241, 1e-240,
+ 1e-239, 1e-238, 1e-237, 1e-236, 1e-235, 1e-234, 1e-233, 1e-232, 1e-231, 1e-230, 1e-229, 1e-228,
+ 1e-227, 1e-226, 1e-225, 1e-224, 1e-223, 1e-222, 1e-221, 1e-220, 1e-219, 1e-218, 1e-217, 1e-216,
+ 1e-215, 1e-214, 1e-213, 1e-212, 1e-211, 1e-210, 1e-209, 1e-208, 1e-207, 1e-206, 1e-205, 1e-204,
+ 1e-203, 1e-202, 1e-201, 1e-200, 1e-199, 1e-198, 1e-197, 1e-196, 1e-195, 1e-194, 1e-193, 1e-192,
+ 1e-191, 1e-190, 1e-189, 1e-188, 1e-187, 1e-186, 1e-185, 1e-184, 1e-183, 1e-182, 1e-181, 1e-180,
+ 1e-179, 1e-178, 1e-177, 1e-176, 1e-175, 1e-174, 1e-173, 1e-172, 1e-171, 1e-170, 1e-169, 1e-168,
+ 1e-167, 1e-166, 1e-165, 1e-164, 1e-163, 1e-162, 1e-161, 1e-160, 1e-159, 1e-158, 1e-157, 1e-156,
+ 1e-155, 1e-154, 1e-153, 1e-152, 1e-151, 1e-150, 1e-149, 1e-148, 1e-147, 1e-146, 1e-145, 1e-144,
+ 1e-143, 1e-142, 1e-141, 1e-140, 1e-139, 1e-138, 1e-137, 1e-136, 1e-135, 1e-134, 1e-133, 1e-132,
+ 1e-131, 1e-130, 1e-129, 1e-128, 1e-127, 1e-126, 1e-125, 1e-124, 1e-123, 1e-122, 1e-121, 1e-120,
+ 1e-119, 1e-118, 1e-117, 1e-116, 1e-115, 1e-114, 1e-113, 1e-112, 1e-111, 1e-110, 1e-109, 1e-108,
+ 1e-107, 1e-106, 1e-105, 1e-104, 1e-103, 1e-102, 1e-101, 1e-100, 1e-99, 1e-98, 1e-97, 1e-96,
+ 1e-95, 1e-94, 1e-93, 1e-92, 1e-91, 1e-90, 1e-89, 1e-88, 1e-87, 1e-86, 1e-85, 1e-84,
+ 1e-83, 1e-82, 1e-81, 1e-80, 1e-79, 1e-78, 1e-77, 1e-76, 1e-75, 1e-74, 1e-73, 1e-72,
+ 1e-71, 1e-70, 1e-69, 1e-68, 1e-67, 1e-66, 1e-65, 1e-64, 1e-63, 1e-62, 1e-61, 1e-60,
+ 1e-59, 1e-58, 1e-57, 1e-56, 1e-55, 1e-54, 1e-53, 1e-52, 1e-51, 1e-50, 1e-49, 1e-48,
+ 1e-47, 1e-46, 1e-45, 1e-44, 1e-43, 1e-42, 1e-41, 1e-40, 1e-39, 1e-38, 1e-37, 1e-36,
+ 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30, 1e-29, 1e-28, 1e-27, 1e-26, 1e-25, 1e-24,
+ 1e-23, 1e-22, 1e-21, 1e-20, 1e-19, 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12,
+ 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0,
+ 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12,
+ 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23, 1e24,
+ 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36,
+ 1e37, 1e38, 1e39, 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48,
+ 1e49, 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, 1e60,
+ 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, 1e70, 1e71, 1e72,
+ 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, 1e80, 1e81, 1e82, 1e83, 1e84,
+ 1e85, 1e86, 1e87, 1e88, 1e89, 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96,
+ 1e97, 1e98, 1e99, 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108,
+ 1e109, 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, 1e120,
+ 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, 1e130, 1e131, 1e132,
+ 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 1e140, 1e141, 1e142, 1e143, 1e144,
+ 1e145, 1e146, 1e147, 1e148, 1e149, 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156,
+ 1e157, 1e158, 1e159, 1e160, 1e161, 1e162, 1e163, 1e164, 1e165, 1e166, 1e167, 1e168,
+ 1e169, 1e170, 1e171, 1e172, 1e173, 1e174, 1e175, 1e176, 1e177, 1e178, 1e179, 1e180,
+ 1e181, 1e182, 1e183, 1e184, 1e185, 1e186, 1e187, 1e188, 1e189, 1e190, 1e191, 1e192,
+ 1e193, 1e194, 1e195, 1e196, 1e197, 1e198, 1e199, 1e200, 1e201, 1e202, 1e203, 1e204,
+ 1e205, 1e206, 1e207, 1e208, 1e209, 1e210, 1e211, 1e212, 1e213, 1e214, 1e215, 1e216,
+ 1e217, 1e218, 1e219, 1e220, 1e221, 1e222, 1e223, 1e224, 1e225, 1e226, 1e227, 1e228,
+ 1e229, 1e230, 1e231, 1e232, 1e233, 1e234, 1e235, 1e236, 1e237, 1e238, 1e239, 1e240,
+ 1e241, 1e242, 1e243, 1e244, 1e245, 1e246, 1e247, 1e248, 1e249, 1e250, 1e251, 1e252,
+ 1e253, 1e254, 1e255, 1e256, 1e257, 1e258, 1e259, 1e260, 1e261, 1e262, 1e263, 1e264,
+ 1e265, 1e266, 1e267, 1e268, 1e269, 1e270, 1e271, 1e272, 1e273, 1e274, 1e275, 1e276,
+ 1e277, 1e278, 1e279, 1e280, 1e281, 1e282, 1e283, 1e284, 1e285, 1e286, 1e287, 1e288,
+ 1e289, 1e290, 1e291, 1e292, 1e293, 1e294, 1e295, 1e296, 1e297, 1e298, 1e299, 1e300,
+ 1e301, 1e302, 1e303, 1e304, 1e305, 1e306, 1e307, 1e308,
+};
+static_assert(SAL_N_ELEMENTS(n10s) == maxExp - minExp + 1);
+
+// return pow(10.0,nExp) optimized for exponents in the interval [-323,308] (i.e., incl. denormals)
+static double getN10Exp(int nExp)
+{
+ if (nExp < minExp || nExp > maxExp)
+ return pow(10.0, static_cast<double>(nExp)); // will return 0 or INF with IEEE 754
+ return n10s[nExp - minExp];
+}
+
+namespace {
+
+/** If value (passed as absolute value) is an integer representable as double,
+ which we handle explicitly at some places.
+ */
+bool isRepresentableInteger(double fAbsValue)
+{
+ assert(fAbsValue >= 0.0);
+ const sal_Int64 kMaxInt = (static_cast< sal_Int64 >(1) << 53) - 1;
+ if (fAbsValue <= static_cast< double >(kMaxInt))
+ {
+ sal_Int64 nInt = static_cast< sal_Int64 >(fAbsValue);
+ // Check the integer range again because double comparison may yield
+ // true within the precision range.
+ // XXX loplugin:fpcomparison complains about floating-point comparison
+ // for static_cast<double>(nInt) == fAbsValue, though we actually want
+ // this here.
+ if (nInt > kMaxInt)
+ return false;
+ double fInt = static_cast< double >(nInt);
+ return !(fInt < fAbsValue) && !(fInt > fAbsValue);
+ }
+ return false;
+}
+
+// Returns 1-based index of least significant bit in a number, or zero if number is zero
+int findFirstSetBit(unsigned n)
+{
+#if defined _WIN32
+ unsigned long pos;
+ unsigned char bNonZero = _BitScanForward(&pos, n);
+ return (bNonZero == 0) ? 0 : pos + 1;
+#else
+ return __builtin_ffs(n);
+#endif
+}
+
+/** Returns number of binary bits for fractional part of the number
+ Expects a proper non-negative double value, not +-INF, not NAN
+ */
+int getBitsInFracPart(double fAbsValue)
+{
+ assert(std::isfinite(fAbsValue) && fAbsValue >= 0.0);
+ if (fAbsValue == 0.0)
+ return 0;
+ auto pValParts = reinterpret_cast< const sal_math_Double * >(&fAbsValue);
+ int nExponent = pValParts->inf_parts.exponent - 1023;
+ if (nExponent >= 52)
+ return 0; // All bits in fraction are in integer part of the number
+ int nLeastSignificant = findFirstSetBit(pValParts->inf_parts.fraction_lo);
+ if (nLeastSignificant == 0)
+ {
+ nLeastSignificant = findFirstSetBit(pValParts->inf_parts.fraction_hi);
+ if (nLeastSignificant == 0)
+ nLeastSignificant = 53; // the implied leading 1 is the least significant
+ else
+ nLeastSignificant += 32;
+ }
+ int nFracSignificant = 53 - nLeastSignificant;
+ int nBitsInFracPart = nFracSignificant - nExponent;
+
+ return std::max(nBitsInFracPart, 0);
+}
+
+}
+
+void SAL_CALL rtl_math_doubleToString(rtl_String ** pResult,
+ sal_Int32 * pResultCapacity,
+ sal_Int32 nResultOffset, double fValue,
+ rtl_math_StringFormat eFormat,
+ sal_Int32 nDecPlaces,
+ char cDecSeparator,
+ sal_Int32 const * pGroups,
+ char cGroupSeparator,
+ sal_Bool bEraseTrailingDecZeros)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::doubleToString(
+ pResult, pResultCapacity, nResultOffset, fValue, eFormat, nDecPlaces,
+ cDecSeparator, pGroups, cGroupSeparator, bEraseTrailingDecZeros);
+}
+
+void SAL_CALL rtl_math_doubleToUString(rtl_uString ** pResult,
+ sal_Int32 * pResultCapacity,
+ sal_Int32 nResultOffset, double fValue,
+ rtl_math_StringFormat eFormat,
+ sal_Int32 nDecPlaces,
+ sal_Unicode cDecSeparator,
+ sal_Int32 const * pGroups,
+ sal_Unicode cGroupSeparator,
+ sal_Bool bEraseTrailingDecZeros)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::doubleToString(
+ pResult, pResultCapacity, nResultOffset, fValue, eFormat, nDecPlaces,
+ cDecSeparator, pGroups, cGroupSeparator, bEraseTrailingDecZeros);
+}
+
+namespace {
+
+template< typename CharT >
+double stringToDouble(CharT const * pBegin, CharT const * pEnd,
+ CharT cDecSeparator, CharT cGroupSeparator,
+ rtl_math_ConversionStatus * pStatus,
+ CharT const ** pParsedEnd)
+{
+ double fVal = 0.0;
+ rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+
+ CharT const * p0 = pBegin;
+ while (p0 != pEnd && (*p0 == CharT(' ') || *p0 == CharT('\t')))
+ {
+ ++p0;
+ }
+
+ bool bSign;
+ bool explicitSign = false;
+ if (p0 != pEnd && *p0 == CharT('-'))
+ {
+ bSign = true;
+ explicitSign = true;
+ ++p0;
+ }
+ else
+ {
+ bSign = false;
+ if (p0 != pEnd && *p0 == CharT('+'))
+ {
+ explicitSign = true;
+ ++p0;
+ }
+ }
+
+ CharT const * p = p0;
+ bool bDone = false;
+
+ // #i112652# XMLSchema-2
+ if ((pEnd - p) >= 3)
+ {
+ if (!explicitSign && (CharT('N') == p[0]) && (CharT('a') == p[1])
+ && (CharT('N') == p[2]))
+ {
+ p += 3;
+ fVal = std::numeric_limits<double>::quiet_NaN();
+ bDone = true;
+ }
+ else if ((CharT('I') == p[0]) && (CharT('N') == p[1])
+ && (CharT('F') == p[2]))
+ {
+ p += 3;
+ fVal = HUGE_VAL;
+ eStatus = rtl_math_ConversionStatus_OutOfRange;
+ bDone = true;
+ }
+ }
+
+ if (!bDone) // do not recognize e.g. NaN1.23
+ {
+ std::unique_ptr<char[]> bufInHeap;
+ std::unique_ptr<const CharT * []> bufInHeapMap;
+ constexpr int bufOnStackSize = 256;
+ char bufOnStack[bufOnStackSize];
+ const CharT* bufOnStackMap[bufOnStackSize];
+ char* buf = bufOnStack;
+ const CharT** bufmap = bufOnStackMap;
+ int bufpos = 0;
+ const size_t bufsize = pEnd - p + (bSign ? 2 : 1);
+ if (bufsize > bufOnStackSize)
+ {
+ bufInHeap = std::make_unique<char[]>(bufsize);
+ bufInHeapMap = std::make_unique<const CharT*[]>(bufsize);
+ buf = bufInHeap.get();
+ bufmap = bufInHeapMap.get();
+ }
+
+ if (bSign)
+ {
+ buf[0] = '-';
+ bufmap[0] = p; // yes, this may be the same pointer as for the next mapping
+ bufpos = 1;
+ }
+ // Put first zero to buffer for strings like "-0"
+ if (p != pEnd && *p == CharT('0'))
+ {
+ buf[bufpos] = '0';
+ bufmap[bufpos] = p;
+ ++bufpos;
+ ++p;
+ }
+ // Leading zeros and group separators between digits may be safely
+ // ignored. p0 < p implies that there was a leading 0 already,
+ // consecutive group separators may not happen as *(p+1) is checked for
+ // digit.
+ while (p != pEnd && (*p == CharT('0') || (*p == cGroupSeparator
+ && p0 < p && p+1 < pEnd && rtl::isAsciiDigit(*(p+1)))))
+ {
+ ++p;
+ }
+
+ // integer part of mantissa
+ for (; p != pEnd; ++p)
+ {
+ CharT c = *p;
+ if (rtl::isAsciiDigit(c))
+ {
+ buf[bufpos] = static_cast<char>(c);
+ bufmap[bufpos] = p;
+ ++bufpos;
+ }
+ else if (c != cGroupSeparator)
+ {
+ break;
+ }
+ else if (p == p0 || (p+1 == pEnd) || !rtl::isAsciiDigit(*(p+1)))
+ {
+ // A leading or trailing (not followed by a digit) group
+ // separator character is not a group separator.
+ break;
+ }
+ }
+
+ // fraction part of mantissa
+ if (p != pEnd && *p == cDecSeparator)
+ {
+ buf[bufpos] = '.';
+ bufmap[bufpos] = p;
+ ++bufpos;
+ ++p;
+
+ for (; p != pEnd; ++p)
+ {
+ CharT c = *p;
+ if (!rtl::isAsciiDigit(c))
+ {
+ break;
+ }
+ buf[bufpos] = static_cast<char>(c);
+ bufmap[bufpos] = p;
+ ++bufpos;
+ }
+ }
+
+ // Exponent
+ if (p != p0 && p != pEnd && (*p == CharT('E') || *p == CharT('e')))
+ {
+ buf[bufpos] = 'E';
+ bufmap[bufpos] = p;
+ ++bufpos;
+ ++p;
+ if (p != pEnd && *p == CharT('-'))
+ {
+ buf[bufpos] = '-';
+ bufmap[bufpos] = p;
+ ++bufpos;
+ ++p;
+ }
+ else if (p != pEnd && *p == CharT('+'))
+ ++p;
+
+ for (; p != pEnd; ++p)
+ {
+ CharT c = *p;
+ if (!rtl::isAsciiDigit(c))
+ break;
+
+ buf[bufpos] = static_cast<char>(c);
+ bufmap[bufpos] = p;
+ ++bufpos;
+ }
+ }
+ else if (p - p0 == 2 && p != pEnd && p[0] == CharT('#')
+ && p[-1] == cDecSeparator && p[-2] == CharT('1'))
+ {
+ if (pEnd - p >= 4 && p[1] == CharT('I') && p[2] == CharT('N')
+ && p[3] == CharT('F'))
+ {
+ // "1.#INF", "+1.#INF", "-1.#INF"
+ p += 4;
+ fVal = HUGE_VAL;
+ eStatus = rtl_math_ConversionStatus_OutOfRange;
+ // Eat any further digits:
+ while (p != pEnd && rtl::isAsciiDigit(*p))
+ ++p;
+ bDone = true;
+ }
+ else if (pEnd - p >= 4 && p[1] == CharT('N') && p[2] == CharT('A')
+ && p[3] == CharT('N'))
+ {
+ // "1.#NAN", "+1.#NAN", "-1.#NAN"
+ p += 4;
+ fVal = std::copysign(std::numeric_limits<double>::quiet_NaN(), bSign ? -1.0 : 1.0);
+ bSign = false; // don't negate again
+
+ // Eat any further digits:
+ while (p != pEnd && rtl::isAsciiDigit(*p))
+ {
+ ++p;
+ }
+ bDone = true;
+ }
+ }
+
+ if (!bDone)
+ {
+ buf[bufpos] = '\0';
+ bufmap[bufpos] = p;
+ char* pCharParseEnd;
+ errno = 0;
+ fVal = strtod_nolocale(buf, &pCharParseEnd);
+ if (errno == ERANGE)
+ {
+ // Check for the dreaded rounded to 15 digits max value
+ // 1.79769313486232e+308 for 1.7976931348623157e+308 we wrote
+ // everywhere, accept with or without plus sign in exponent.
+ const char* b = buf;
+ if (b[0] == '-')
+ ++b;
+ if (((pCharParseEnd - b == 21) || (pCharParseEnd - b == 20))
+ && !strncmp( b, "1.79769313486232", 16)
+ && (b[16] == 'e' || b[16] == 'E')
+ && (((pCharParseEnd - b == 21) && !strncmp( b+17, "+308", 4))
+ || ((pCharParseEnd - b == 20) && !strncmp( b+17, "308", 3))))
+ {
+ fVal = (buf < b) ? -DBL_MAX : DBL_MAX;
+ }
+ else
+ {
+ eStatus = rtl_math_ConversionStatus_OutOfRange;
+ }
+ }
+ p = bufmap[pCharParseEnd - buf];
+ bSign = false;
+ }
+ }
+
+ // overflow also if more than DBL_MAX_10_EXP digits without decimal
+ // separator, or 0. and more than DBL_MIN_10_EXP digits, ...
+ bool bHuge = fVal == HUGE_VAL; // g++ 3.0.1 requires it this way...
+ if (bHuge)
+ eStatus = rtl_math_ConversionStatus_OutOfRange;
+
+ if (bSign)
+ fVal = -fVal;
+
+ if (pStatus)
+ *pStatus = eStatus;
+
+ if (pParsedEnd)
+ *pParsedEnd = p == p0 ? pBegin : p;
+
+ return fVal;
+}
+
+}
+
+double SAL_CALL rtl_math_stringToDouble(char const * pBegin,
+ char const * pEnd,
+ char cDecSeparator,
+ char cGroupSeparator,
+ rtl_math_ConversionStatus * pStatus,
+ char const ** pParsedEnd)
+ SAL_THROW_EXTERN_C()
+{
+ return stringToDouble(
+ reinterpret_cast<unsigned char const *>(pBegin),
+ reinterpret_cast<unsigned char const *>(pEnd),
+ static_cast<unsigned char>(cDecSeparator),
+ static_cast<unsigned char>(cGroupSeparator), pStatus,
+ reinterpret_cast<unsigned char const **>(pParsedEnd));
+}
+
+double SAL_CALL rtl_math_uStringToDouble(sal_Unicode const * pBegin,
+ sal_Unicode const * pEnd,
+ sal_Unicode cDecSeparator,
+ sal_Unicode cGroupSeparator,
+ rtl_math_ConversionStatus * pStatus,
+ sal_Unicode const ** pParsedEnd)
+ SAL_THROW_EXTERN_C()
+{
+ return stringToDouble(pBegin, pEnd, cDecSeparator, cGroupSeparator, pStatus,
+ pParsedEnd);
+}
+
+double SAL_CALL rtl_math_round(double fValue, int nDecPlaces,
+ enum rtl_math_RoundingMode eMode)
+ SAL_THROW_EXTERN_C()
+{
+ if (!std::isfinite(fValue))
+ return fValue;
+
+ if (fValue == 0.0)
+ return fValue;
+
+ if (nDecPlaces == 0)
+ {
+ switch (eMode)
+ {
+ case rtl_math_RoundingMode_Corrected:
+ return std::round(fValue);
+ case rtl_math_RoundingMode_HalfEven:
+ if (const int oldMode = std::fegetround(); std::fesetround(FE_TONEAREST) == 0)
+ {
+ fValue = std::nearbyint(fValue);
+ std::fesetround(oldMode);
+ return fValue;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ const double fOrigValue = fValue;
+
+ // sign adjustment
+ bool bSign = std::signbit( fValue );
+ if (bSign)
+ fValue = -fValue;
+
+ // Rounding to decimals between integer distance precision (gaps) does not
+ // make sense, do not even try to multiply/divide and introduce inaccuracy.
+ // For same reasons, do not attempt to round integers to decimals.
+ if (nDecPlaces >= 0
+ && (fValue >= 0x1p52
+ || isRepresentableInteger(fValue)))
+ return fOrigValue;
+
+ double fFac = 0;
+ if (nDecPlaces != 0)
+ {
+ if (nDecPlaces > 0)
+ {
+ // Determine how many decimals are representable in the precision.
+ // Anything greater 2^52 and 0.0 was already ruled out above.
+ // Theoretically 0.5, 0.25, 0.125, 0.0625, 0.03125, ...
+ const sal_math_Double* pd = reinterpret_cast<const sal_math_Double*>(&fValue);
+ const sal_Int32 nDec = 52 - (pd->parts.exponent - 1023);
+
+ if (nDec <= 0)
+ {
+ assert(!"Shouldn't this had been caught already as large number?");
+ return fOrigValue;
+ }
+
+ if (nDec < nDecPlaces)
+ nDecPlaces = nDec;
+ }
+
+ // Avoid 1e-5 (1.0000000000000001e-05) and such inaccurate fractional
+ // factors that later when dividing back spoil things. For negative
+ // decimals divide first with the inverse, then multiply the rounded
+ // value back.
+ fFac = getN10Exp(abs(nDecPlaces));
+
+ if (fFac == 0.0 || (nDecPlaces < 0 && !std::isfinite(fFac)))
+ // Underflow, rounding to that many integer positions would be 0.
+ return 0.0;
+
+ if (!std::isfinite(fFac))
+ // Overflow with very small values and high number of decimals.
+ return fOrigValue;
+
+ if (nDecPlaces < 0)
+ fValue /= fFac;
+ else
+ fValue *= fFac;
+
+ if (!std::isfinite(fValue))
+ return fOrigValue;
+ }
+
+ // Round only if not already in distance precision gaps of integers, where
+ // for [2^52,2^53) adding 0.5 would even yield the next representable
+ // integer.
+ if (fValue < 0x1p52)
+ {
+ switch ( eMode )
+ {
+ case rtl_math_RoundingMode_Corrected :
+ fValue = rtl::math::approxFloor(fValue + 0.5);
+ break;
+ case rtl_math_RoundingMode_Down:
+ fValue = rtl::math::approxFloor(fValue);
+ break;
+ case rtl_math_RoundingMode_Up:
+ fValue = rtl::math::approxCeil(fValue);
+ break;
+ case rtl_math_RoundingMode_Floor:
+ fValue = bSign ? rtl::math::approxCeil(fValue)
+ : rtl::math::approxFloor( fValue );
+ break;
+ case rtl_math_RoundingMode_Ceiling:
+ fValue = bSign ? rtl::math::approxFloor(fValue)
+ : rtl::math::approxCeil(fValue);
+ break;
+ case rtl_math_RoundingMode_HalfDown :
+ {
+ double f = floor(fValue);
+ fValue = ((fValue - f) <= 0.5) ? f : ceil(fValue);
+ }
+ break;
+ case rtl_math_RoundingMode_HalfUp:
+ {
+ double f = floor(fValue);
+ fValue = ((fValue - f) < 0.5) ? f : ceil(fValue);
+ }
+ break;
+ case rtl_math_RoundingMode_HalfEven:
+#if defined FLT_ROUNDS
+ /*
+ Use fast version. FLT_ROUNDS may be defined to a function by some compilers!
+
+ DBL_EPSILON is the smallest fractional number which can be represented,
+ its reciprocal is therefore the smallest number that cannot have a
+ fractional part. Once you add this reciprocal to `x', its fractional part
+ is stripped off. Simply subtracting the reciprocal back out returns `x'
+ without its fractional component.
+ Simple, clever, and elegant - thanks to Ross Cottrell, the original author,
+ who placed it into public domain.
+
+ volatile: prevent compiler from being too smart
+ */
+ if (FLT_ROUNDS == 1)
+ {
+ volatile double x = fValue + 1.0 / DBL_EPSILON;
+ fValue = x - 1.0 / DBL_EPSILON;
+ }
+ else
+#endif // FLT_ROUNDS
+ {
+ double f = floor(fValue);
+ if ((fValue - f) != 0.5)
+ {
+ fValue = floor( fValue + 0.5 );
+ }
+ else
+ {
+ double g = f / 2.0;
+ fValue = (g == floor( g )) ? f : (f + 1.0);
+ }
+ }
+ break;
+ default:
+ OSL_ASSERT(false);
+ break;
+ }
+ }
+
+ if (nDecPlaces != 0)
+ {
+ if (nDecPlaces < 0)
+ fValue *= fFac;
+ else
+ fValue /= fFac;
+ }
+
+ if (!std::isfinite(fValue))
+ return fOrigValue;
+
+ return bSign ? -fValue : fValue;
+}
+
+double SAL_CALL rtl_math_pow10Exp(double fValue, int nExp) SAL_THROW_EXTERN_C()
+{
+ return fValue * getN10Exp(nExp);
+}
+
+double SAL_CALL rtl_math_approxValue( double fValue ) SAL_THROW_EXTERN_C()
+{
+ const double fBigInt = 0x1p41; // 2^41 -> only 11 bits left for fractional part, fine as decimal
+ if (fValue == 0.0 || fValue == HUGE_VAL || !std::isfinite( fValue) || fValue > fBigInt)
+ {
+ // We don't handle these conditions. Bail out.
+ return fValue;
+ }
+
+ double fOrigValue = fValue;
+
+ bool bSign = std::signbit(fValue);
+ if (bSign)
+ fValue = -fValue;
+
+ // If the value is either integer representable as double,
+ // or only has small number of bits in fraction part, then we need not do any approximation
+ if (isRepresentableInteger(fValue) || getBitsInFracPart(fValue) <= 11)
+ return fOrigValue;
+
+ int nExp = static_cast< int >(floor(log10(fValue)));
+ nExp = 14 - nExp;
+ double fExpValue = getN10Exp(abs(nExp));
+
+ if (nExp < 0)
+ fValue /= fExpValue;
+ else
+ fValue *= fExpValue;
+
+ // If the original value was near DBL_MIN we got an overflow. Restore and
+ // bail out.
+ if (!std::isfinite(fValue))
+ return fOrigValue;
+
+ fValue = std::round(fValue);
+
+ if (nExp < 0)
+ fValue *= fExpValue;
+ else
+ fValue /= fExpValue;
+
+ // If the original value was near DBL_MAX we got an overflow. Restore and
+ // bail out.
+ if (!std::isfinite(fValue))
+ return fOrigValue;
+
+ return bSign ? -fValue : fValue;
+}
+
+bool SAL_CALL rtl_math_approxEqual(double a, double b) SAL_THROW_EXTERN_C()
+{
+ static const double e48 = 0x1p-48;
+ static const double e44 = 0x1p-44;
+
+ if (a == b)
+ return true;
+
+ if (a == 0.0 || b == 0.0)
+ return false;
+
+ const double d = fabs(a - b);
+ if (!std::isfinite(d))
+ return false; // Nan or Inf involved
+
+ a = fabs(a);
+ if (d > (a * e44))
+ return false;
+ b = fabs(b);
+ if (d > (b * e44))
+ return false;
+
+ if (isRepresentableInteger(d) && isRepresentableInteger(a) && isRepresentableInteger(b))
+ return false; // special case for representable integers.
+
+ return (d < a * e48 && d < b * e48);
+}
+
+double SAL_CALL rtl_math_expm1(double fValue) SAL_THROW_EXTERN_C()
+{
+ return expm1(fValue);
+}
+
+double SAL_CALL rtl_math_log1p(double fValue) SAL_THROW_EXTERN_C()
+{
+#ifdef __APPLE__
+ if (fValue == -0.0)
+ return fValue; // macOS 10.8 libc returns 0.0 for -0.0
+#endif
+
+ return log1p(fValue);
+}
+
+double SAL_CALL rtl_math_atanh(double fValue) SAL_THROW_EXTERN_C()
+{
+ return ::atanh(fValue);
+}
+
+/** Parent error function (erf) */
+double SAL_CALL rtl_math_erf(double x) SAL_THROW_EXTERN_C()
+{
+ return erf(x);
+}
+
+/** Parent complementary error function (erfc) */
+double SAL_CALL rtl_math_erfc(double x) SAL_THROW_EXTERN_C()
+{
+ return erfc(x);
+}
+
+/** improved accuracy of asinh for |x| large and for x near zero
+ @see #i97605#
+ */
+double SAL_CALL rtl_math_asinh(double fX) SAL_THROW_EXTERN_C()
+{
+ if ( fX == 0.0 )
+ return 0.0;
+
+ double fSign = 1.0;
+ if ( fX < 0.0 )
+ {
+ fX = - fX;
+ fSign = -1.0;
+ }
+
+ if ( fX < 0.125 )
+ return fSign * rtl_math_log1p( fX + fX*fX / (1.0 + sqrt( 1.0 + fX*fX)));
+
+ if ( fX < 1.25e7 )
+ return fSign * log( fX + sqrt( 1.0 + fX*fX));
+
+ return fSign * log( 2.0*fX);
+}
+
+/** improved accuracy of acosh for x large and for x near 1
+ @see #i97605#
+ */
+double SAL_CALL rtl_math_acosh(double fX) SAL_THROW_EXTERN_C()
+{
+ volatile double fZ = fX - 1.0;
+ if (fX < 1.0)
+ return std::numeric_limits<double>::quiet_NaN();
+ if ( fX == 1.0 )
+ return 0.0;
+
+ if ( fX < 1.1 )
+ return rtl_math_log1p( fZ + sqrt( fZ*fZ + 2.0*fZ));
+
+ if ( fX < 1.25e7 )
+ return log( fX + sqrt( fX*fX - 1.0));
+
+ return log( 2.0*fX);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/random.cxx b/sal/rtl/random.cxx
new file mode 100644
index 000000000..418358b22
--- /dev/null
+++ b/sal/rtl/random.cxx
@@ -0,0 +1,310 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cmath>
+
+#include <sal/types.h>
+#include <o3tl/temporary.hxx>
+#include <osl/thread.h>
+#include <osl/thread.hxx>
+#include <osl/time.h>
+#include <rtl/alloc.h>
+#include <rtl/digest.h>
+#include <rtl/random.h>
+#include <oslrandom.h>
+
+#define RTL_RANDOM_RNG_1(a) ((a) * 16807L)
+#define RTL_RANDOM_RNG_2(a) ((a) * 65539L)
+
+#define RTL_RANDOM_RNG(x, y, z) \
+{ \
+ (x) = 170 * ((x) % 178) - 63 * ((x) / 178); \
+ if ((x) < 0) (x) += 30328; \
+ \
+ (y) = 171 * ((y) % 177) - 2 * ((y) / 177); \
+ if ((y) < 0) (y) += 30269; \
+ \
+ (z) = 172 * ((z) % 176) - 35 * ((z) / 176); \
+ if ((z) < 0) (z) += 30307; \
+}
+
+namespace {
+
+struct RandomData_Impl
+{
+ sal_Int16 m_nX;
+ sal_Int16 m_nY;
+ sal_Int16 m_nZ;
+};
+
+}
+
+static double data (RandomData_Impl *pImpl);
+
+#define RTL_RANDOM_DIGEST rtl_Digest_AlgorithmMD5
+#define RTL_RANDOM_SIZE_DIGEST RTL_DIGEST_LENGTH_MD5
+#define RTL_RANDOM_SIZE_POOL 1023
+
+namespace {
+
+struct RandomPool_Impl
+{
+ rtlDigest m_hDigest;
+ sal_uInt8 m_pDigest[RTL_RANDOM_SIZE_DIGEST];
+ sal_uInt8 m_pData[RTL_RANDOM_SIZE_POOL + 1];
+ sal_uInt32 m_nData;
+ sal_uInt32 m_nIndex;
+ sal_uInt32 m_nCount;
+};
+
+}
+
+static bool initPool(RandomPool_Impl *pImpl);
+
+static void seedPool(
+ RandomPool_Impl *pImpl, const sal_uInt8 *pBuffer, sal_Size nBufLen);
+
+static void readPool(
+ RandomPool_Impl *pImpl, sal_uInt8 *pBuffer, sal_Size nBufLen);
+
+static double data(RandomData_Impl *pImpl)
+{
+ double random;
+
+ RTL_RANDOM_RNG (pImpl->m_nX, pImpl->m_nY, pImpl->m_nZ);
+ random = ((static_cast<double>(pImpl->m_nX) / 30328.0) +
+ (static_cast<double>(pImpl->m_nY) / 30269.0) +
+ (static_cast<double>(pImpl->m_nZ) / 30307.0) );
+
+ return std::modf(random, &o3tl::temporary(double()));
+}
+
+static bool initPool(RandomPool_Impl *pImpl)
+{
+ pImpl->m_hDigest = rtl_digest_create(RTL_RANDOM_DIGEST);
+ if (pImpl->m_hDigest)
+ {
+ oslThreadIdentifier tid;
+ TimeValue tv;
+ RandomData_Impl rd;
+ double seed;
+
+ /* The use of uninitialized stack variables as a way to
+ * enhance the entropy of the random pool triggers
+ * memory checkers like purify and valgrind.
+ */
+
+ /*
+ seedPool (pImpl, (sal_uInt8*)&tid, sizeof(tid));
+ seedPool (pImpl, (sal_uInt8*)&tv, sizeof(tv));
+ seedPool (pImpl, (sal_uInt8*)&rd, sizeof(rd));
+ */
+
+ tid = osl::Thread::getCurrentIdentifier();
+ tid = RTL_RANDOM_RNG_2(RTL_RANDOM_RNG_1(tid));
+ seedPool (pImpl, reinterpret_cast< sal_uInt8* >(&tid), sizeof(tid));
+
+ osl_getSystemTime (&tv);
+ tv.Seconds = RTL_RANDOM_RNG_2(tv.Seconds);
+ tv.Nanosec = RTL_RANDOM_RNG_2(tv.Nanosec);
+ seedPool (pImpl, reinterpret_cast< sal_uInt8* >(&tv), sizeof(tv));
+
+ rd.m_nX = static_cast<sal_Int16>(((tid >> 1) << 1) + 1);
+ rd.m_nY = static_cast<sal_Int16>(((tv.Seconds >> 1) << 1) + 1);
+ rd.m_nZ = static_cast<sal_Int16>(((tv.Nanosec >> 1) << 1) + 1);
+ seedPool (pImpl, reinterpret_cast< sal_uInt8* >(&rd), sizeof(rd));
+
+ while (pImpl->m_nData < RTL_RANDOM_SIZE_POOL)
+ {
+ seed = data (&rd);
+ seedPool (pImpl, reinterpret_cast< sal_uInt8* >(&seed), sizeof(seed));
+ }
+ return true;
+ }
+ return false;
+}
+
+static void seedPool(
+ RandomPool_Impl *pImpl, const sal_uInt8 *pBuffer, sal_Size nBufLen)
+{
+ sal_Size i;
+ sal_sSize j, k;
+
+ for (i = 0; i < nBufLen; i += RTL_RANDOM_SIZE_DIGEST)
+ {
+ j = nBufLen - i;
+ if (j > RTL_RANDOM_SIZE_DIGEST)
+ j = RTL_RANDOM_SIZE_DIGEST;
+
+ rtl_digest_update(
+ pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST);
+
+ k = (pImpl->m_nIndex + j) - RTL_RANDOM_SIZE_POOL;
+ if (k > 0)
+ {
+ rtl_digest_update(
+ pImpl->m_hDigest, &(pImpl->m_pData[pImpl->m_nIndex]), j - k);
+ rtl_digest_update(
+ pImpl->m_hDigest, &(pImpl->m_pData[0]), k);
+ }
+ else
+ {
+ rtl_digest_update(
+ pImpl->m_hDigest, &(pImpl->m_pData[pImpl->m_nIndex]), j);
+ }
+
+ rtl_digest_update(pImpl->m_hDigest, pBuffer, j);
+ pBuffer += j;
+
+ rtl_digest_get(
+ pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST);
+ for (k = 0; k < j; k++)
+ {
+ pImpl->m_pData[pImpl->m_nIndex++] ^= pImpl->m_pDigest[k];
+ if (pImpl->m_nIndex >= RTL_RANDOM_SIZE_POOL)
+ {
+ pImpl->m_nData = RTL_RANDOM_SIZE_POOL;
+ pImpl->m_nIndex = 0;
+ }
+ }
+ }
+
+ if (pImpl->m_nIndex > pImpl->m_nData)
+ pImpl->m_nData = pImpl->m_nIndex;
+}
+
+static void readPool (
+ RandomPool_Impl *pImpl, sal_uInt8 *pBuffer, sal_Size nBufLen)
+{
+ sal_Int32 j, k;
+
+ while (nBufLen > 0)
+ {
+ j = nBufLen;
+ if (j > RTL_RANDOM_SIZE_DIGEST/2)
+ j = RTL_RANDOM_SIZE_DIGEST/2;
+ nBufLen -= j;
+
+ rtl_digest_update(
+ pImpl->m_hDigest,
+ &(pImpl->m_pDigest[RTL_RANDOM_SIZE_DIGEST/2]),
+ RTL_RANDOM_SIZE_DIGEST/2);
+
+ k = (pImpl->m_nIndex + j) - pImpl->m_nData;
+ if (k > 0)
+ {
+ rtl_digest_update(
+ pImpl->m_hDigest, &(pImpl->m_pData[pImpl->m_nIndex]), j - k);
+ rtl_digest_update(
+ pImpl->m_hDigest, &(pImpl->m_pData[0]), k);
+ }
+ else
+ {
+ rtl_digest_update(
+ pImpl->m_hDigest, &(pImpl->m_pData[pImpl->m_nIndex]), j);
+ }
+
+ rtl_digest_get(
+ pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST);
+ for (k = 0; k < j; k++)
+ {
+ if (pImpl->m_nIndex >= pImpl->m_nData)
+ pImpl->m_nIndex = 0;
+
+ pImpl->m_pData[pImpl->m_nIndex++] ^= pImpl->m_pDigest[k];
+ *pBuffer++ = pImpl->m_pDigest[k + RTL_RANDOM_SIZE_DIGEST/2];
+ }
+ }
+
+ pImpl->m_nCount++;
+ rtl_digest_update(
+ pImpl->m_hDigest, &(pImpl->m_nCount), sizeof(pImpl->m_nCount));
+ rtl_digest_update(
+ pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST);
+ rtl_digest_get(
+ pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST);
+}
+
+rtlRandomPool SAL_CALL rtl_random_createPool() SAL_THROW_EXTERN_C()
+{
+ /* try to get system random number, if it fail fall back on own pool */
+ RandomPool_Impl *pImpl = static_cast< RandomPool_Impl* >(rtl_allocateZeroMemory(sizeof(RandomPool_Impl)));
+ if (pImpl)
+ {
+ char sanity[4];
+ if (!osl_get_system_random_data(sanity, 4))
+ {
+ if (!initPool(pImpl))
+ {
+ rtl_freeZeroMemory(pImpl, sizeof(RandomPool_Impl));
+ pImpl = nullptr;
+ }
+ }
+ }
+ return static_cast< rtlRandomPool >(pImpl);
+}
+
+void SAL_CALL rtl_random_destroyPool(rtlRandomPool Pool) SAL_THROW_EXTERN_C()
+{
+ RandomPool_Impl *pImpl = static_cast< RandomPool_Impl* >(Pool);
+ if (pImpl)
+ {
+ if (pImpl->m_hDigest)
+ rtl_digest_destroy(pImpl->m_hDigest);
+
+ rtl_freeZeroMemory (pImpl, sizeof(RandomPool_Impl));
+ }
+}
+
+rtlRandomError SAL_CALL rtl_random_addBytes(
+ rtlRandomPool Pool, const void *Buffer, sal_Size Bytes) SAL_THROW_EXTERN_C()
+{
+ RandomPool_Impl *pImpl = static_cast< RandomPool_Impl* >(Pool);
+ const sal_uInt8 *pBuffer = static_cast< const sal_uInt8* >(Buffer);
+
+ if (!pImpl || !pBuffer)
+ return rtl_Random_E_Argument;
+
+ if (pImpl->m_hDigest)
+ seedPool (pImpl, pBuffer, Bytes);
+
+ return rtl_Random_E_None;
+}
+
+rtlRandomError SAL_CALL rtl_random_getBytes (
+ rtlRandomPool Pool, void *Buffer, sal_Size Bytes) SAL_THROW_EXTERN_C()
+{
+ RandomPool_Impl *pImpl = static_cast< RandomPool_Impl* >(Pool);
+ sal_uInt8 *pBuffer = static_cast< sal_uInt8* >(Buffer);
+
+ if (!pImpl || !pBuffer)
+ return rtl_Random_E_Argument;
+
+ if (pImpl->m_hDigest || !osl_get_system_random_data(static_cast< char* >(Buffer), Bytes))
+ {
+ if (!pImpl->m_hDigest && !initPool(pImpl))
+ return rtl_Random_E_Unknown;
+ readPool(pImpl, pBuffer, Bytes);
+ }
+ return rtl_Random_E_None;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/rtl_process.cxx b/sal/rtl/rtl_process.cxx
new file mode 100644
index 000000000..76963dc13
--- /dev/null
+++ b/sal/rtl/rtl_process.cxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cstring>
+
+#include <rtl/process.h>
+#include <rtl/uuid.h>
+#include <sal/types.h>
+
+namespace
+{
+
+class Id
+{
+public:
+ Id() { rtl_createUuid(uuid_, nullptr, false); }
+
+ Id(const Id&) = delete;
+ Id& operator=(const Id&) = delete;
+
+ void copy(sal_uInt8 * target) const
+ { std::memcpy(target, uuid_, UUID_SIZE); }
+
+private:
+ enum { UUID_SIZE = 16 };
+
+ sal_uInt8 uuid_[UUID_SIZE];
+};
+
+} // end namespace
+
+void rtl_getGlobalProcessId(sal_uInt8 * pTargetUUID)
+{
+ static Id theId;
+ theId.copy(pTargetUUID);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/strbuf.cxx b/sal/rtl/strbuf.cxx
new file mode 100644
index 000000000..7ba25ff4a
--- /dev/null
+++ b/sal/rtl/strbuf.cxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+
+#include <osl/interlck.h>
+#include <rtl/strbuf.hxx>
+#include "strimp.hxx"
+
+#if USE_SDT_PROBES
+#define RTL_LOG_STRING_BITS 8
+#endif
+
+#include "strtmpl.hxx"
+
+/*************************************************************************
+ * rtl_stringbuffer_newFromStr_WithLength
+ */
+void SAL_CALL rtl_stringbuffer_newFromStr_WithLength( rtl_String ** newStr,
+ const char * value,
+ sal_Int32 count )
+{
+ rtl::str::stringbuffer_newFromStr_WithLength(newStr, value, count);
+}
+
+/*************************************************************************
+ * rtl_stringbuffer_newFromStringBuffer
+ */
+sal_Int32 SAL_CALL rtl_stringbuffer_newFromStringBuffer( rtl_String ** newStr,
+ sal_Int32 capacity,
+ rtl_String * oldStr )
+{
+ return rtl::str::stringbuffer_newFromStringBuffer(newStr, capacity, oldStr);
+}
+
+/*************************************************************************
+ * rtl_stringbuffer_ensureCapacity
+ */
+void SAL_CALL rtl_stringbuffer_ensureCapacity
+ (rtl_String ** This, sal_Int32* capacity, sal_Int32 minimumCapacity)
+{
+ rtl::str::stringbuffer_ensureCapacity(This, capacity, minimumCapacity);
+}
+
+/*************************************************************************
+ * rtl_stringbuffer_insert
+ */
+void SAL_CALL rtl_stringbuffer_insert( rtl_String ** This,
+ sal_Int32 * capacity,
+ sal_Int32 offset,
+ const char * str,
+ sal_Int32 len )
+{
+ rtl::str::stringbuffer_insert(This, capacity, offset, str, len);
+}
+
+/*************************************************************************
+ * rtl_stringbuffer_remove
+ */
+void SAL_CALL rtl_stringbuffer_remove( rtl_String ** This,
+ sal_Int32 start,
+ sal_Int32 len )
+{
+ rtl::str::stringbuffer_remove(This, start, len);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/strimp.cxx b/sal/rtl/strimp.cxx
new file mode 100644
index 000000000..44b56023d
--- /dev/null
+++ b/sal/rtl/strimp.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <sal/config.h>
+
+#include <assert.h>
+#include <rtl/alloc.h>
+#include <rtl/ustring.h>
+#include <rtllifecycle.h>
+
+#include "strimp.hxx"
+#include "alloc_impl.hxx"
+#include "alloc_arena.hxx"
+
+/*
+ * TODO: add a slower, more awful, but more space efficient
+ * custom allocator for the pre-init phase. Existing slab
+ * allocator's minimum alloc size is 24bytes, and by default
+ * is 32 bytes.
+ */
+static rtl_arena_type *pre_arena = nullptr;
+
+rtl_allocateStringFn rtl_allocateString = malloc;
+rtl_freeStringFn rtl_freeString = free;
+
+extern "C" {
+static void *pre_allocateStringFn(size_t n)
+{
+ sal_Size size = RTL_MEMORY_ALIGN(n + 4, 4);
+ char *addr = static_cast<char*>(rtl_arena_alloc(pre_arena, &size));
+ assert(size>= 12);
+ reinterpret_cast<sal_uInt32*>(addr)[0] = size - 12;
+ return addr + 4;
+}
+
+static void pre_freeStringFn(void *data)
+{
+ char *addr = static_cast<char*>(data) - 4;
+ sal_uInt32 size = reinterpret_cast<sal_uInt32*>(addr)[0] + 12;
+
+ rtl_arena_free(pre_arena, addr, size);
+}
+} // extern "C"
+
+static void mark_static(void *addr, sal_Size /* size */)
+{
+ char *inner = static_cast<char*>(addr) + 4;
+ rtl_uString *str = reinterpret_cast<rtl_uString *>(inner);
+ str->refCount |= SAL_STRING_STATIC_FLAG;
+}
+
+void SAL_CALL rtl_alloc_preInit (sal_Bool start) SAL_THROW_EXTERN_C()
+{
+ if (start)
+ {
+ rtl_allocateString = pre_allocateStringFn;
+ rtl_freeString = pre_freeStringFn;
+ pre_arena = rtl_arena_create("pre-init strings", 4, 0,
+ nullptr, rtl_arena_alloc,
+ rtl_arena_free, 0);
+
+ // To be consistent (and to ensure the rtl_cache threads are started).
+ ensureCacheSingleton();
+ }
+ else
+ {
+ rtl_arena_foreach(pre_arena, mark_static);
+ rtl_allocateString = malloc;
+ rtl_freeString = free;
+
+ // TODO: also re-initialize main allocator as well.
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/strimp.hxx b/sal/rtl/strimp.hxx
new file mode 100644
index 000000000..69404e0aa
--- /dev/null
+++ b/sal/rtl/strimp.hxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SAL_RTL_STRIMP_HXX
+#define INCLUDED_SAL_RTL_STRIMP_HXX
+
+#include <config_probes.h>
+#if USE_SDT_PROBES
+#include <sys/sdt.h>
+#endif
+
+#include <sal/types.h>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+
+/* ======================================================================= */
+/* Help functions for String and UString */
+/* ======================================================================= */
+
+/*
+ * refCount is opaque and includes 2 bit-fields;
+ * MSB: 'interned' - is stored in the intern hash
+ * MSB-1: 'static' - is a const / static string,
+ * do no ref counting
+ */
+#define SAL_STRING_INTERN_FLAG 0x80000000
+#define SAL_STRING_STATIC_FLAG 0x40000000
+#define SAL_STRING_REFCOUNT(a) ((a) & 0x3fffffff)
+
+#define SAL_STRING_IS_INTERN(a) ((a)->refCount & SAL_STRING_INTERN_FLAG)
+#define SAL_STRING_IS_STATIC(a) ((a)->refCount & SAL_STRING_STATIC_FLAG)
+
+rtl_uString* rtl_uString_ImplAlloc( sal_Int32 nLen );
+
+rtl_String* rtl_string_ImplAlloc( sal_Int32 nLen );
+
+extern "C" {
+
+typedef void *(SAL_CALL * rtl_allocateStringFn)(size_t size);
+typedef void (*rtl_freeStringFn)(void *);
+
+}
+
+extern rtl_allocateStringFn rtl_allocateString;
+extern rtl_freeStringFn rtl_freeString;
+
+// string lifetime instrumentation / diagnostics
+#if USE_SDT_PROBES
+# define PROBE_SNAME(n,b) n ## _ ## b
+# define PROBE_NAME(n,b) PROBE_SNAME(n,b)
+# define PROBE_NEW PROBE_NAME (new_string,RTL_LOG_STRING_BITS)
+# define PROBE_DEL PROBE_NAME (delete_string,RTL_LOG_STRING_BITS)
+# define PROBE_INTERN_NEW PROBE_NAME (new_string_intern,RTL_LOG_STRING_BITS)
+# define PROBE_INTERN_DEL PROBE_NAME (delete_string_intern,RTL_LOG_STRING_BITS)
+# define RTL_LOG_STRING_NEW(s) \
+ DTRACE_PROBE4(libreoffice, PROBE_NEW, s, \
+ (s)->refCount, (s)->length, (s)->buffer)
+# define RTL_LOG_STRING_DELETE(s) \
+ DTRACE_PROBE4(libreoffice, PROBE_DEL, s, \
+ (s)->refCount, (s)->length, (s)->buffer)
+# define RTL_LOG_STRING_INTERN_NEW(s,o) \
+ DTRACE_PROBE5(libreoffice, PROBE_INTERN_NEW, s, \
+ (s)->refCount, (s)->length, (s)->buffer, o)
+# define RTL_LOG_STRING_INTERN_DELETE(s) \
+ DTRACE_PROBE4(libreoffice, PROBE_INTERN_DEL, s, \
+ (s)->refCount, (s)->length, (s)->buffer)
+#else
+# define RTL_LOG_STRING_NEW(s)
+# define RTL_LOG_STRING_DELETE(s)
+# define RTL_LOG_STRING_INTERN_NEW(s,o)
+# define RTL_LOG_STRING_INTERN_DELETE(s)
+#endif /* USE_SDT_PROBES */
+
+#endif // INCLUDED_SAL_RTL_STRIMP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/string.cxx b/sal/rtl/string.cxx
new file mode 100644
index 000000000..23196e528
--- /dev/null
+++ b/sal/rtl/string.cxx
@@ -0,0 +1,667 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstdlib>
+
+#include <osl/interlck.h>
+#include <osl/diagnose.h>
+#include <rtl/tencinfo.h>
+
+#include "strimp.hxx"
+#include <rtl/character.hxx>
+#include <rtl/string.h>
+
+#include <rtl/math.h>
+
+#if defined _WIN32
+// Temporary check to verify that the #pragma pack around rtl_String is indeed cargo cult and can
+// safely be removed:
+static_assert(alignof (rtl_String) == 4);
+static_assert(sizeof (rtl_String) == 12);
+#endif
+
+/* ======================================================================= */
+
+#if USE_SDT_PROBES
+#define RTL_LOG_STRING_BITS 8
+#endif
+
+#include "strtmpl.hxx"
+
+/* ======================================================================= */
+
+sal_Int32 SAL_CALL rtl_str_valueOfFloat(char * pStr, float f)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfFP<RTL_STR_MAX_VALUEOFFLOAT>(pStr, f);
+}
+
+sal_Int32 SAL_CALL rtl_str_valueOfDouble(char * pStr, double d)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfFP<RTL_STR_MAX_VALUEOFDOUBLE>(pStr, d);
+}
+
+float SAL_CALL rtl_str_toFloat(char const * pStr) SAL_THROW_EXTERN_C()
+{
+ assert(pStr);
+ return static_cast<float>(rtl_math_stringToDouble(pStr, pStr + rtl_str_getLength(pStr),
+ '.', 0, nullptr, nullptr));
+}
+
+double SAL_CALL rtl_str_toDouble(char const * pStr) SAL_THROW_EXTERN_C()
+{
+ assert(pStr);
+ return rtl_math_stringToDouble(pStr, pStr + rtl_str_getLength(pStr), '.', 0,
+ nullptr, nullptr);
+}
+
+/* ======================================================================= */
+
+static int rtl_ImplGetFastUTF8ByteLen( const sal_Unicode* pStr, sal_Int32 nLen )
+{
+ int n;
+ sal_Unicode c;
+ sal_uInt32 nUCS4Char;
+ const sal_Unicode* pEndStr;
+
+ n = 0;
+ pEndStr = pStr+nLen;
+ while ( pStr < pEndStr )
+ {
+ c = *pStr;
+
+ if ( c < 0x80 )
+ n++;
+ else if ( c < 0x800 )
+ n += 2;
+ else
+ {
+ if ( !rtl::isHighSurrogate(c) )
+ n += 3;
+ else
+ {
+ nUCS4Char = c;
+
+ if ( pStr+1 < pEndStr )
+ {
+ c = *(pStr+1);
+ if ( rtl::isLowSurrogate(c) )
+ {
+ nUCS4Char = rtl::combineSurrogates(nUCS4Char, c);
+ pStr++;
+ }
+ }
+
+ if ( nUCS4Char < 0x10000 )
+ n += 3;
+ else if ( nUCS4Char < 0x200000 )
+ n += 4;
+ else if ( nUCS4Char < 0x4000000 )
+ n += 5;
+ else
+ n += 6;
+ }
+ }
+
+ pStr++;
+ }
+
+ return n;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static bool rtl_impl_convertUStringToString(rtl_String ** pTarget,
+ sal_Unicode const * pSource,
+ sal_Int32 nLength,
+ rtl_TextEncoding nEncoding,
+ sal_uInt32 nFlags,
+ bool bCheckErrors)
+{
+ assert(pTarget != nullptr);
+ assert(pSource != nullptr || nLength == 0);
+ assert(nLength >= 0);
+ OSL_ASSERT(nLength == 0 || rtl_isOctetTextEncoding(nEncoding));
+
+ if ( !nLength )
+ rtl_string_new( pTarget );
+ else
+ {
+ rtl_String* pTemp;
+ rtl_UnicodeToTextConverter hConverter;
+ sal_uInt32 nInfo;
+ sal_Size nSrcChars;
+ sal_Size nDestBytes;
+ sal_Size nNewLen;
+ sal_Size nNotConvertedChars;
+ sal_Size nMaxCharLen;
+
+ /* Optimization for UTF-8 - we try to calculate the exact length */
+ /* For all other encoding we try a good estimation */
+ if ( nEncoding == RTL_TEXTENCODING_UTF8 )
+ {
+ nNewLen = rtl_ImplGetFastUTF8ByteLen( pSource, nLength );
+ /* Includes the string only ASCII, then we could copy
+ the buffer faster */
+ if ( nNewLen == static_cast<sal_Size>(nLength) )
+ {
+ char* pBuffer;
+ if ( *pTarget )
+ rtl_string_release( *pTarget );
+ *pTarget = rtl_string_ImplAlloc( nLength );
+ OSL_ASSERT(*pTarget != nullptr);
+ pBuffer = (*pTarget)->buffer;
+ do
+ {
+ /* Check ASCII range */
+ OSL_ENSURE( *pSource <= 127,
+ "rtl_uString2String() - UTF8 test is encoding is wrong" );
+
+ *pBuffer = static_cast<char>(static_cast<unsigned char>(*pSource));
+ pBuffer++;
+ pSource++;
+ nLength--;
+ }
+ while ( nLength );
+ return true;
+ }
+
+ nMaxCharLen = 4;
+ }
+ else
+ {
+ rtl_TextEncodingInfo aTextEncInfo;
+ aTextEncInfo.StructSize = sizeof( aTextEncInfo );
+ if ( !rtl_getTextEncodingInfo( nEncoding, &aTextEncInfo ) )
+ {
+ aTextEncInfo.AverageCharSize = 1;
+ aTextEncInfo.MaximumCharSize = 8;
+ }
+
+ nNewLen = nLength * static_cast<sal_Size>(aTextEncInfo.AverageCharSize);
+ nMaxCharLen = aTextEncInfo.MaximumCharSize;
+ }
+
+ nFlags |= RTL_UNICODETOTEXT_FLAGS_FLUSH;
+ hConverter = rtl_createUnicodeToTextConverter( nEncoding );
+
+ for (;;)
+ {
+ pTemp = rtl_string_ImplAlloc( nNewLen );
+ OSL_ASSERT(pTemp != nullptr);
+ nDestBytes = rtl_convertUnicodeToText( hConverter, nullptr,
+ pSource, nLength,
+ pTemp->buffer, nNewLen,
+ nFlags,
+ &nInfo, &nSrcChars );
+ if (bCheckErrors && (nInfo & RTL_UNICODETOTEXT_INFO_ERROR) != 0)
+ {
+ rtl_freeString(pTemp);
+ rtl_destroyUnicodeToTextConverter(hConverter);
+ return false;
+ }
+
+ if ((nInfo & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) == 0)
+ break;
+
+ /* Buffer not big enough, try again with enough space */
+ rtl_freeString( pTemp );
+
+ /* Try with the max. count of characters with
+ additional overhead for replacing functionality */
+ nNotConvertedChars = nLength-nSrcChars;
+ nNewLen = nDestBytes+(nNotConvertedChars*nMaxCharLen)+nNotConvertedChars+4;
+ }
+
+ /* Set the buffer to the correct size or is there to
+ much overhead, reallocate to the correct size */
+ if ( nNewLen > nDestBytes+8 )
+ {
+ rtl_String* pTemp2 = rtl_string_ImplAlloc( nDestBytes );
+ OSL_ASSERT(pTemp2 != nullptr);
+ rtl::str::Copy(pTemp2->buffer, pTemp->buffer, nDestBytes);
+ rtl_freeString( pTemp );
+ pTemp = pTemp2;
+ }
+ else
+ {
+ pTemp->length = nDestBytes;
+ pTemp->buffer[nDestBytes] = 0;
+ }
+
+ rtl_destroyUnicodeToTextConverter( hConverter );
+ if ( *pTarget )
+ rtl_string_release( *pTarget );
+ *pTarget = pTemp;
+
+ /* Results the conversion in an empty buffer -
+ create an empty string */
+ if ( pTemp && !nDestBytes )
+ rtl_string_new( pTarget );
+ }
+ return true;
+}
+
+void SAL_CALL rtl_uString2String( rtl_String** ppThis,
+ const sal_Unicode* pUStr,
+ sal_Int32 nULen,
+ rtl_TextEncoding eTextEncoding,
+ sal_uInt32 nCvtFlags )
+ SAL_THROW_EXTERN_C()
+{
+ rtl_impl_convertUStringToString(ppThis, pUStr, nULen, eTextEncoding,
+ nCvtFlags, false);
+}
+
+sal_Bool SAL_CALL rtl_convertUStringToString(rtl_String ** pTarget,
+ sal_Unicode const * pSource,
+ sal_Int32 nLength,
+ rtl_TextEncoding nEncoding,
+ sal_uInt32 nFlags)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl_impl_convertUStringToString(pTarget, pSource, nLength, nEncoding,
+ nFlags, true);
+}
+
+void rtl_string_newReplaceFirst(
+ rtl_String ** newStr, rtl_String * str, char const * from,
+ sal_Int32 fromLength, char const * to, sal_Int32 toLength,
+ sal_Int32 * index) SAL_THROW_EXTERN_C()
+{
+ assert(str != nullptr);
+ assert(index != nullptr);
+ assert(*index >= 0 && *index <= str->length);
+ assert(fromLength >= 0);
+ assert(toLength >= 0);
+ sal_Int32 i = rtl_str_indexOfStr_WithLength(
+ str->buffer + *index, str->length - *index, from, fromLength);
+ if (i == -1) {
+ rtl_string_assign(newStr, str);
+ } else {
+ assert(i <= str->length - *index);
+ i += *index;
+ assert(fromLength <= str->length);
+ if (str->length - fromLength > SAL_MAX_INT32 - toLength) {
+ std::abort();
+ }
+ rtl::str::newReplaceStrAt(newStr, str, i, fromLength, to, toLength);
+ }
+ *index = i;
+}
+
+void rtl_string_newReplaceAll(
+ rtl_String ** newStr, rtl_String * str, char const * from,
+ sal_Int32 fromLength, char const * to, sal_Int32 toLength)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceAllFromIndex(newStr, str, from, fromLength, to, toLength, 0);
+}
+
+sal_Int32 SAL_CALL rtl_str_getLength(const char* pStr) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::getLength(pStr);
+}
+
+sal_Int32 SAL_CALL rtl_str_compare(const char* pStr1, const char* pStr2) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::null_terminated(pStr1), rtl::str::null_terminated(pStr2),
+ rtl::str::CompareNormal(), rtl::str::noShortening);
+}
+
+sal_Int32 SAL_CALL rtl_str_compare_WithLength(const char* pStr1, sal_Int32 nStr1Len,
+ const char* pStr2, sal_Int32 nStr2Len)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len),
+ rtl::str::with_length(pStr2, nStr2Len),
+ rtl::str::CompareNormal(), rtl::str::noShortening);
+}
+
+sal_Int32 SAL_CALL rtl_str_shortenedCompare_WithLength(const char* pStr1, sal_Int32 nStr1Len,
+ const char* pStr2, sal_Int32 nStr2Len,
+ sal_Int32 nShortenedLength)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len),
+ rtl::str::with_length(pStr2, nStr2Len),
+ rtl::str::CompareNormal(), nShortenedLength);
+}
+
+sal_Int32 SAL_CALL rtl_str_reverseCompare_WithLength(const char* pStr1, sal_Int32 nStr1Len,
+ const char* pStr2, sal_Int32 nStr2Len)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::reverseCompare_WithLengths(pStr1, nStr1Len, pStr2, nStr2Len,
+ rtl::str::CompareNormal());
+}
+
+sal_Int32 SAL_CALL rtl_str_compareIgnoreAsciiCase(const char* pStr1, const char* pStr2)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::null_terminated(pStr1), rtl::str::null_terminated(pStr2),
+ rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening);
+}
+
+sal_Int32 SAL_CALL rtl_str_compareIgnoreAsciiCase_WithLength(const char* pStr1, sal_Int32 nStr1Len,
+ const char* pStr2, sal_Int32 nStr2Len)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len),
+ rtl::str::with_length(pStr2, nStr2Len),
+ rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening);
+}
+
+sal_Int32 SAL_CALL rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
+ const char* pStr1, sal_Int32 nStr1Len, const char* pStr2, sal_Int32 nStr2Len,
+ sal_Int32 nShortenedLength) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len),
+ rtl::str::with_length(pStr2, nStr2Len),
+ rtl::str::CompareIgnoreAsciiCase(), nShortenedLength);
+}
+
+sal_Int32 SAL_CALL rtl_str_hashCode(const char* pStr) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::hashCode(pStr);
+}
+
+sal_Int32 SAL_CALL rtl_str_hashCode_WithLength(const char* pStr, sal_Int32 nLen)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::hashCode_WithLength(pStr, nLen);
+}
+
+sal_Int32 SAL_CALL rtl_str_indexOfChar(const char* pStr, char c) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::indexOfChar(pStr, c);
+}
+
+sal_Int32 SAL_CALL rtl_str_indexOfChar_WithLength(const char* pStr, sal_Int32 nLen, char c)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::indexOfChar_WithLength(pStr, nLen, c);
+}
+
+sal_Int32 SAL_CALL rtl_str_lastIndexOfChar(const char* pStr, char c) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::lastIndexOfChar(pStr, c);
+}
+
+sal_Int32 SAL_CALL rtl_str_lastIndexOfChar_WithLength(const char* pStr, sal_Int32 nLen, char c)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::lastIndexOfChar_WithLength(pStr, nLen, c);
+}
+
+sal_Int32 SAL_CALL rtl_str_indexOfStr(const char* pStr, const char* pSubStr) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::indexOfStr(pStr, pSubStr);
+}
+
+sal_Int32 SAL_CALL rtl_str_indexOfStr_WithLength(const char* pStr, sal_Int32 nStrLen,
+ const char* pSubStr, sal_Int32 nSubLen)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::indexOfStr_WithLength(pStr, nStrLen, pSubStr, nSubLen);
+}
+
+sal_Int32 SAL_CALL rtl_str_lastIndexOfStr(const char* pStr, const char* pSubStr)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::lastIndexOfStr(pStr, pSubStr);
+}
+
+sal_Int32 SAL_CALL rtl_str_lastIndexOfStr_WithLength(const char* pStr, sal_Int32 nStrLen,
+ const char* pSubStr, sal_Int32 nSubLen)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::lastIndexOfStr_WithLength(pStr, nStrLen, pSubStr, nSubLen);
+}
+
+void SAL_CALL rtl_str_replaceChar(char* pStr, char cOld, char cNew) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::replaceChars(rtl::str::null_terminated(pStr), rtl::str::FromTo(cOld, cNew));
+}
+
+void SAL_CALL rtl_str_replaceChar_WithLength(char* pStr, sal_Int32 nLen, char cOld, char cNew)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::replaceChars(rtl::str::with_length(pStr, nLen), rtl::str::FromTo(cOld, cNew));
+}
+
+void SAL_CALL rtl_str_toAsciiLowerCase(char* pStr) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::replaceChars(rtl::str::null_terminated(pStr), rtl::str::toAsciiLower);
+}
+
+void SAL_CALL rtl_str_toAsciiLowerCase_WithLength(char* pStr, sal_Int32 nLen) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::replaceChars(rtl::str::with_length(pStr, nLen), rtl::str::toAsciiLower);
+}
+
+void SAL_CALL rtl_str_toAsciiUpperCase(char* pStr) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::replaceChars(rtl::str::null_terminated(pStr), rtl::str::toAsciiUpper);
+}
+
+void SAL_CALL rtl_str_toAsciiUpperCase_WithLength(char* pStr, sal_Int32 nLen) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::replaceChars(rtl::str::with_length(pStr, nLen), rtl::str::toAsciiUpper);
+}
+
+sal_Int32 SAL_CALL rtl_str_trim(char* pStr) SAL_THROW_EXTERN_C() { return rtl::str::trim(pStr); }
+
+sal_Int32 SAL_CALL rtl_str_trim_WithLength(char* pStr, sal_Int32 nLen) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::trim_WithLength(pStr, nLen);
+}
+
+sal_Int32 SAL_CALL rtl_str_valueOfBoolean(char* pStr, sal_Bool b) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfBoolean(pStr, b);
+}
+
+sal_Int32 SAL_CALL rtl_str_valueOfChar(char* pStr, char c) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfChar(pStr, c);
+}
+
+sal_Int32 SAL_CALL rtl_str_valueOfInt32(char* pStr, sal_Int32 n, sal_Int16 nRadix)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfInt<RTL_STR_MAX_VALUEOFINT32>(pStr, n, nRadix);
+}
+
+sal_Int32 SAL_CALL rtl_str_valueOfInt64(char* pStr, sal_Int64 n, sal_Int16 nRadix)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfInt<RTL_STR_MAX_VALUEOFINT64>(pStr, n, nRadix);
+}
+
+sal_Int32 SAL_CALL rtl_str_valueOfUInt64(char* pStr, sal_uInt64 n, sal_Int16 nRadix)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfInt<RTL_STR_MAX_VALUEOFUINT64>(pStr, n, nRadix);
+}
+
+sal_Bool SAL_CALL rtl_str_toBoolean(const char* pStr) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::toBoolean(pStr);
+}
+
+sal_Int32 SAL_CALL rtl_str_toInt32(const char* pStr, sal_Int16 nRadix) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::toInt<sal_Int32>(rtl::str::null_terminated(pStr), nRadix);
+}
+
+sal_Int64 SAL_CALL rtl_str_toInt64(const char* pStr, sal_Int16 nRadix) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::toInt<sal_Int64>(rtl::str::null_terminated(pStr), nRadix);
+}
+
+sal_Int64 SAL_CALL rtl_str_toInt64_WithLength(const char* pStr, sal_Int16 nRadix,
+ sal_Int32 nStrLength) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::toInt<sal_Int64>(rtl::str::with_length(pStr, nStrLength), nRadix);
+}
+
+sal_uInt32 SAL_CALL rtl_str_toUInt32(const char* pStr, sal_Int16 nRadix) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::toInt<sal_uInt32>(rtl::str::null_terminated(pStr), nRadix);
+}
+
+sal_uInt64 SAL_CALL rtl_str_toUInt64(const char* pStr, sal_Int16 nRadix) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::toInt<sal_uInt64>(rtl::str::null_terminated(pStr), nRadix);
+}
+
+rtl_String* rtl_string_ImplAlloc(sal_Int32 nLen) { return rtl::str::Alloc<rtl_String>(nLen); }
+
+void SAL_CALL rtl_string_acquire(rtl_String* pThis) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::acquire(pThis);
+}
+
+void SAL_CALL rtl_string_release(rtl_String* pThis) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::release(pThis);
+}
+
+void SAL_CALL rtl_string_new(rtl_String** ppThis) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::new_(ppThis);
+}
+
+rtl_String* SAL_CALL rtl_string_alloc(sal_Int32 nLen) SAL_THROW_EXTERN_C()
+{
+ assert(nLen >= 0);
+ return rtl::str::Alloc<rtl_String>(nLen);
+}
+
+void SAL_CALL rtl_string_new_WithLength(rtl_String** ppThis, sal_Int32 nLen) SAL_THROW_EXTERN_C()
+{
+ rtl::str::new_WithLength(ppThis, nLen);
+}
+
+void SAL_CALL rtl_string_newFromString(rtl_String** ppThis, const rtl_String* pStr)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newFromString(ppThis, pStr);
+}
+
+void SAL_CALL rtl_string_newFromStr(rtl_String** ppThis, const char* pCharStr) SAL_THROW_EXTERN_C()
+{
+ rtl::str::newFromStr(ppThis, pCharStr);
+}
+
+void SAL_CALL rtl_string_newFromStr_WithLength(rtl_String** ppThis, const char* pCharStr,
+ sal_Int32 nLen) SAL_THROW_EXTERN_C()
+{
+ rtl::str::newFromStr_WithLength(ppThis, pCharStr, nLen);
+}
+
+void SAL_CALL rtl_string_newFromSubString(rtl_String** ppThis, const rtl_String* pFrom,
+ sal_Int32 beginIndex, sal_Int32 count)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newFromSubString(ppThis, pFrom, beginIndex, count);
+}
+
+// Used when creating from string literals.
+void SAL_CALL rtl_string_newFromLiteral(rtl_String** ppThis, const char* pCharStr, sal_Int32 nLen,
+ sal_Int32 allocExtra) SAL_THROW_EXTERN_C()
+{
+ rtl::str::newFromStr_WithLength(ppThis, pCharStr, nLen, allocExtra);
+}
+
+void SAL_CALL rtl_string_assign(rtl_String** ppThis, rtl_String* pStr) SAL_THROW_EXTERN_C()
+{
+ rtl::str::assign(ppThis, pStr);
+}
+
+sal_Int32 SAL_CALL rtl_string_getLength(const rtl_String* pThis) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::getLength(pThis);
+}
+
+char* SAL_CALL rtl_string_getStr(rtl_String* pThis) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::getStr(pThis);
+}
+
+void SAL_CALL rtl_string_newConcat(rtl_String** ppThis, rtl_String* pLeft, rtl_String* pRight)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newConcat(ppThis, pLeft, pRight);
+}
+
+void SAL_CALL rtl_string_ensureCapacity(rtl_String** ppThis, sal_Int32 size) SAL_THROW_EXTERN_C()
+{
+ rtl::str::ensureCapacity(ppThis, size);
+}
+
+void SAL_CALL rtl_string_newReplaceStrAt(rtl_String** ppThis, rtl_String* pStr, sal_Int32 nIndex,
+ sal_Int32 nCount, rtl_String* pNewSubStr)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceStrAt(ppThis, pStr, nIndex, nCount, pNewSubStr);
+}
+
+void SAL_CALL rtl_string_newReplaceStrAt_WithLength(rtl_String** ppThis, rtl_String* pStr, sal_Int32 nIndex,
+ sal_Int32 nCount, char const * subStr, sal_Int32 substrLen)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceStrAt(ppThis, pStr, nIndex, nCount, subStr, substrLen);
+}
+
+void SAL_CALL rtl_string_newReplace(rtl_String** ppThis, rtl_String* pStr, char cOld, char cNew)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplace(ppThis, pStr, cOld, cNew);
+}
+
+void SAL_CALL rtl_string_newToAsciiLowerCase(rtl_String** ppThis, rtl_String* pStr)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceChars<rtl::str::ToAsciiLower>(ppThis, pStr);
+}
+
+void SAL_CALL rtl_string_newToAsciiUpperCase(rtl_String** ppThis, rtl_String* pStr)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceChars<rtl::str::ToAsciiUpper>(ppThis, pStr);
+}
+
+void SAL_CALL rtl_string_newTrim(rtl_String** ppThis, rtl_String* pStr) SAL_THROW_EXTERN_C()
+{
+ rtl::str::newTrim(ppThis, pStr);
+}
+
+sal_Int32 SAL_CALL rtl_string_getToken(rtl_String** ppThis, rtl_String* pStr, sal_Int32 nToken,
+ char cTok, sal_Int32 nIndex) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::getToken(ppThis, pStr, nToken, cTok, nIndex);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/strtmpl.hxx b/sal/rtl/strtmpl.hxx
new file mode 100644
index 000000000..e1389ce6a
--- /dev/null
+++ b/sal/rtl/strtmpl.hxx
@@ -0,0 +1,1904 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include <cwchar>
+#include <limits>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+
+#include "strimp.hxx"
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <rtl/character.hxx>
+#include <rtl/math.h>
+#include <rtl/string.h>
+#include <rtl/ustring.h>
+
+#include <dragonbox/dragonbox.h>
+
+void internRelease(rtl_uString*);
+
+namespace rtl::str
+{
+template <typename C> auto IMPL_RTL_USTRCODE(C c) { return std::make_unsigned_t<C>(c); }
+
+// Wrappers around null-terminated/known-length strings, that allow to generalize algorithms
+// without overhead (e.g., without need to get length of null-terminated strings).
+
+template <typename C> struct null_terminated
+{
+ C* p;
+ null_terminated(C* pStr)
+ : p(pStr)
+ {
+ assert(pStr);
+ }
+ auto begin() const { return p; }
+ struct EndDetector
+ {
+ friend bool operator==(EndDetector, C* iter) { return *iter == 0; }
+ friend bool operator==(C* iter, EndDetector) { return *iter == 0; }
+ friend bool operator!=(EndDetector, C* iter) { return *iter != 0; }
+ friend bool operator!=(C* iter, EndDetector) { return *iter != 0; }
+ };
+ static auto end() { return EndDetector{}; }
+};
+
+template <typename C> struct with_length
+{
+ C* p;
+ sal_Int32 len;
+ with_length(C* pStr, sal_Int32 nLength)
+ : p(pStr)
+ , len(nLength)
+ {
+ assert(len >= 0);
+ }
+ auto begin() const { return p; }
+ auto end() const { return p + len; }
+};
+
+struct ToAsciiLower
+{
+ template <typename C> static bool Applicable(C c)
+ {
+ return rtl::isAsciiUpperCase(IMPL_RTL_USTRCODE(c));
+ }
+ template <typename C> static C Replace(C c)
+ {
+ return rtl::toAsciiLowerCase(IMPL_RTL_USTRCODE(c));
+ }
+} constexpr toAsciiLower;
+
+struct ToAsciiUpper
+{
+ template <typename C> static bool Applicable(C c)
+ {
+ return rtl::isAsciiLowerCase(IMPL_RTL_USTRCODE(c));
+ }
+ template <typename C> static C Replace(C c)
+ {
+ return rtl::toAsciiUpperCase(IMPL_RTL_USTRCODE(c));
+ }
+} constexpr toAsciiUpper;
+
+template <typename C> struct FromTo
+{
+ C from;
+ C to;
+ FromTo(C cFrom, C cTo) : from(cFrom), to(cTo) {}
+ C Replace(C c) const { return c == from ? to : c; }
+};
+
+template <typename C> void Copy(C* _pDest, const C* _pSrc, sal_Int32 _nCount)
+{
+ // take advantage of builtin optimisations
+ std::copy(_pSrc, _pSrc + _nCount, _pDest);
+}
+
+template <typename C> void CopyBackward(C* _pDest, const C* _pSrc, sal_Int32 _nCount)
+{
+ // take advantage of builtin optimisations
+ std::copy_backward(_pSrc, _pSrc + _nCount, _pDest + _nCount);
+}
+
+inline void Copy(sal_Unicode* _pDest, const char* _pSrc, sal_Int32 _nCount)
+{
+ std::transform(_pSrc, _pSrc + _nCount, _pDest,
+ [](char c)
+ {
+ assert(rtl::isAscii(static_cast<unsigned char>(c)));
+ SAL_WARN_IF(c == '\0', "rtl.string", "Found embedded \\0 ASCII character");
+ return static_cast<unsigned char>(c);
+ });
+}
+
+inline sal_Int16 implGetDigit(sal_Unicode ch, sal_Int16 nRadix)
+{
+ sal_Int16 n = -1;
+ if ((ch >= '0') && (ch <= '9'))
+ n = ch - '0';
+ else if ((ch >= 'a') && (ch <= 'z'))
+ n = ch - 'a' + 10;
+ else if ((ch >= 'A') && (ch <= 'Z'))
+ n = ch - 'A' + 10;
+ return (n < nRadix) ? n : -1;
+}
+
+inline bool implIsWhitespace(sal_Unicode c)
+{
+ /* Space or Control character? */
+ if ((c <= 32) && c)
+ return true;
+
+ /* Only in the General Punctuation area Space or Control characters are included? */
+ if ((c < 0x2000) || (c > 0x206F))
+ return false;
+
+ if (((c >= 0x2000) && (c <= 0x200B)) || /* All Spaces */
+ (c == 0x2028) || /* LINE SEPARATOR */
+ (c == 0x2029)) /* PARAGRAPH SEPARATOR */
+ return true;
+
+ return false;
+}
+
+/* ======================================================================= */
+/* C-String functions which could be used without the String-Class */
+/* ======================================================================= */
+
+template <typename T> sal_Int32 getLength( const T* pStr )
+{
+ assert(pStr);
+ if constexpr (std::is_class_v<T>)
+ {
+ return pStr->length;
+ }
+ else
+ {
+ // take advantage of builtin optimisations
+ return std::char_traits<T>::length(pStr);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename C> void warnIfCharAndNotAscii(C c)
+{
+ if constexpr (sizeof(c) == sizeof(char))
+ SAL_WARN_IF(!rtl::isAscii(static_cast<unsigned char>(c)), "rtl.string",
+ "Found non-ASCII char");
+}
+
+template <typename C1, typename C2> void warnIfOneIsCharAndNotAscii(C1 c1, C2 c2)
+{
+ if constexpr (sizeof(c1) != sizeof(c2))
+ {
+ warnIfCharAndNotAscii(c1);
+ warnIfCharAndNotAscii(c2);
+ }
+}
+
+struct CompareNormal
+{
+ template <typename C1, typename C2> static sal_Int32 compare(C1 c1, C2 c2)
+ {
+ warnIfOneIsCharAndNotAscii(c1, c2);
+ return static_cast<sal_Int32>(IMPL_RTL_USTRCODE(c1))
+ - static_cast<sal_Int32>(IMPL_RTL_USTRCODE(c2));
+ }
+};
+
+struct CompareIgnoreAsciiCase
+{
+ template <typename C1, typename C2> static sal_Int32 compare(C1 c1, C2 c2)
+ {
+ warnIfOneIsCharAndNotAscii(c1, c2);
+ return rtl::compareIgnoreAsciiCase(IMPL_RTL_USTRCODE(c1), IMPL_RTL_USTRCODE(c2));
+ }
+};
+
+/* ----------------------------------------------------------------------- */
+
+struct NoShortening
+{
+ constexpr bool operator>=(int) { return true; } // for assert
+ constexpr bool operator==(int) { return false; } // for loop break check
+ constexpr void operator--() {} // for decrement in loop
+} constexpr noShortening;
+
+template <class S1, class S2, class Compare, typename Shorten_t>
+sal_Int32 compare(S1 s1, S2 s2, Compare, Shorten_t shortenedLength)
+{
+ static_assert(std::is_same_v<Shorten_t, NoShortening> || std::is_same_v<Shorten_t, sal_Int32>);
+ assert(shortenedLength >= 0);
+ auto pStr1 = s1.begin();
+ const auto end1 = s1.end();
+ auto pStr2 = s2.begin();
+ const auto end2 = s2.end();
+ for (;;)
+ {
+ if (shortenedLength == 0)
+ return 0;
+ if (pStr2 == end2)
+ return pStr1 == end1 ? 0 : 1;
+ if (pStr1 == end1)
+ return -1;
+ if (const sal_Int32 nRet = Compare::compare(*pStr1, *pStr2))
+ return nRet;
+ --shortenedLength;
+ ++pStr1;
+ ++pStr2;
+ }
+}
+
+// take advantage of builtin optimisations
+template <typename C, std::enable_if_t<sizeof(C) == sizeof(wchar_t), int> = 0>
+sal_Int32 compare(null_terminated<C> s1, null_terminated<C> s2, CompareNormal, NoShortening)
+{
+ return wcscmp(reinterpret_cast<wchar_t const*>(s1.p), reinterpret_cast<wchar_t const*>(s2.p));
+}
+template <typename C, std::enable_if_t<sizeof(C) == sizeof(char), int> = 0>
+sal_Int32 compare(null_terminated<C> s1, null_terminated<C> s2, CompareNormal, NoShortening)
+{
+ return strcmp(reinterpret_cast<char const*>(s1.p), reinterpret_cast<char const*>(s2.p));
+}
+template <typename C>
+sal_Int32 compare(with_length<C> s1, with_length<C> s2, CompareNormal, NoShortening)
+{
+ std::basic_string_view sv1(s1.p, s1.len);
+ return sv1.compare(std::basic_string_view(s2.p, s2.len));
+}
+template <typename C1, typename C2, class Compare>
+sal_Int32 compare(with_length<C1> s1, with_length<C2> s2, Compare cf, sal_Int32 nShortenedLength)
+{
+ assert(nShortenedLength >= 0);
+ s1.len = std::min(s1.len, nShortenedLength);
+ s2.len = std::min(s2.len, nShortenedLength);
+ return compare(s1, s2, cf, noShortening);
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename C1, typename C2, class Compare>
+sal_Int32 reverseCompare_WithLengths(const C1* pStr1, sal_Int32 nStr1Len,
+ const C2* pStr2, sal_Int32 nStr2Len, Compare)
+{
+ assert(pStr1 || nStr1Len == 0);
+ assert(nStr1Len >= 0);
+ assert(pStr2 || nStr2Len == 0);
+ assert(nStr2Len >= 0);
+ const C1* pStr1Run = pStr1+nStr1Len;
+ const C2* pStr2Run = pStr2+nStr2Len;
+ while ((pStr1 < pStr1Run) && (pStr2 < pStr2Run))
+ {
+ pStr1Run--;
+ pStr2Run--;
+ if (const sal_Int32 nRet = Compare::compare(*pStr1Run, *pStr2Run))
+ return nRet;
+ }
+
+ return nStr1Len - nStr2Len;
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 hashCode_WithLength(const IMPL_RTL_STRCODE*, sal_Int32);
+
+template <typename IMPL_RTL_STRCODE> sal_Int32 hashCode( const IMPL_RTL_STRCODE* pStr )
+{
+ return hashCode_WithLength( pStr, getLength( pStr ) );
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 hashCode_WithLength ( const IMPL_RTL_STRCODE* pStr,
+ sal_Int32 nLen )
+{
+ assert(nLen >= 0);
+ sal_uInt32 h = static_cast<sal_uInt32>(nLen);
+ while ( nLen > 0 )
+ {
+ h = (h*37U) + IMPL_RTL_USTRCODE( *pStr );
+ pStr++;
+ nLen--;
+ }
+ return static_cast<sal_Int32>(h);
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 indexOfChar ( const IMPL_RTL_STRCODE* pStr,
+ IMPL_RTL_STRCODE c )
+{
+ assert(pStr);
+ if (!c)
+ return -1; // Unifies behavior of strchr/wcschr and unoptimized algorithm wrt '\0'
+
+ if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(char))
+ {
+ // take advantage of builtin optimisations
+ const IMPL_RTL_STRCODE* p = strchr(pStr, c);
+ return p ? p - pStr : -1;
+ }
+ else if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t))
+ {
+ // take advantage of builtin optimisations
+ wchar_t const * p = wcschr(reinterpret_cast<wchar_t const *>(pStr), static_cast<wchar_t>(c));
+ return p ? p - reinterpret_cast<wchar_t const *>(pStr) : -1;
+ }
+ else
+ {
+ const IMPL_RTL_STRCODE* pTempStr = pStr;
+ while ( *pTempStr )
+ {
+ if ( *pTempStr == c )
+ return pTempStr-pStr;
+
+ pTempStr++;
+ }
+
+ return -1;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 indexOfChar_WithLength ( const IMPL_RTL_STRCODE* pStr,
+ sal_Int32 nLen,
+ IMPL_RTL_STRCODE c )
+{
+// assert(nLen >= 0);
+ if (nLen <= 0)
+ return -1;
+ // take advantage of builtin optimisations
+ using my_string_view = std::basic_string_view<IMPL_RTL_STRCODE>;
+ my_string_view v(pStr, nLen);
+ typename my_string_view::size_type idx = v.find(c);
+ return idx == my_string_view::npos ? -1 : idx;
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 lastIndexOfChar_WithLength(const IMPL_RTL_STRCODE*, sal_Int32, IMPL_RTL_STRCODE);
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 lastIndexOfChar ( const IMPL_RTL_STRCODE* pStr,
+ IMPL_RTL_STRCODE c )
+{
+ assert(pStr);
+ if (!c)
+ return -1; // Unifies behavior of strrchr/wcsrchr and lastIndexOfChar_WithLength wrt '\0'
+
+ if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(char))
+ {
+ // take advantage of builtin optimisations
+ const IMPL_RTL_STRCODE* p = strrchr(pStr, c);
+ return p ? p - pStr : -1;
+ }
+ else if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t))
+ {
+ // take advantage of builtin optimisations
+ wchar_t const * p = wcsrchr(reinterpret_cast<wchar_t const *>(pStr), static_cast<wchar_t>(c));
+ return p ? p - reinterpret_cast<wchar_t const *>(pStr) : -1;
+ }
+ else
+ {
+ return lastIndexOfChar_WithLength( pStr, getLength( pStr ), c );
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 lastIndexOfChar_WithLength ( const IMPL_RTL_STRCODE* pStr,
+ sal_Int32 nLen,
+ IMPL_RTL_STRCODE c )
+{
+ assert(nLen >= 0);
+ // take advantage of builtin optimisations
+ using my_string_view = std::basic_string_view<IMPL_RTL_STRCODE>;
+ my_string_view v(pStr, nLen);
+ typename my_string_view::size_type idx = v.rfind(c);
+ return idx == my_string_view::npos ? -1 : idx;
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 indexOfStr_WithLength(const IMPL_RTL_STRCODE*, sal_Int32, const IMPL_RTL_STRCODE*,
+ sal_Int32);
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 indexOfStr ( const IMPL_RTL_STRCODE* pStr,
+ const IMPL_RTL_STRCODE* pSubStr )
+{
+ assert(pStr);
+ assert(pSubStr);
+ /* an empty SubString is always not findable */
+ if (*pSubStr == 0)
+ return -1;
+ if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(char))
+ {
+ // take advantage of builtin optimisations
+ const IMPL_RTL_STRCODE* p = strstr(pStr, pSubStr);
+ return p ? p - pStr : -1;
+ }
+ else if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t))
+ {
+ // take advantage of builtin optimisations
+ wchar_t const * p = wcsstr(reinterpret_cast<wchar_t const *>(pStr), reinterpret_cast<wchar_t const *>(pSubStr));
+ return p ? p - reinterpret_cast<wchar_t const *>(pStr) : -1;
+ }
+ else
+ {
+ return indexOfStr_WithLength( pStr, getLength( pStr ),
+ pSubStr, getLength( pSubStr ) );
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 indexOfStr_WithLength ( const IMPL_RTL_STRCODE* pStr,
+ sal_Int32 nStrLen,
+ const IMPL_RTL_STRCODE* pSubStr,
+ sal_Int32 nSubLen )
+{
+ assert(nStrLen >= 0);
+ assert(nSubLen >= 0);
+ /* an empty SubString is always not findable */
+ if ( nSubLen == 0 )
+ return -1;
+ // take advantage of builtin optimisations
+ using my_string_view = std::basic_string_view<IMPL_RTL_STRCODE>;
+ my_string_view v(pStr, nStrLen);
+ auto idx = nSubLen == 1 ? v.find(*pSubStr) : v.find(pSubStr, 0, nSubLen);
+ return idx == my_string_view::npos ? -1 : idx;
+}
+
+inline sal_Int32 indexOfStr_WithLength(const sal_Unicode* pStr, sal_Int32 nStrLen,
+ const char* pSubStr, sal_Int32 nSubLen)
+{
+ assert(nStrLen >= 0);
+ assert(nSubLen >= 0);
+ if (nSubLen > 0 && nSubLen <= nStrLen)
+ {
+ sal_Unicode const* end = pStr + nStrLen;
+ sal_Unicode const* cursor = pStr;
+
+ while (cursor < end)
+ {
+ cursor = std::char_traits<sal_Unicode>::find(cursor, end - cursor, *pSubStr);
+ if (!cursor || (end - cursor < nSubLen))
+ {
+ /* no enough left to actually have a match */
+ break;
+ }
+ /* now it is worth trying a full match */
+ if (nSubLen == 1 || rtl_ustr_asciil_reverseEquals_WithLength(cursor, pSubStr, nSubLen))
+ {
+ return cursor - pStr;
+ }
+ cursor += 1;
+ }
+ }
+ return -1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 lastIndexOfStr_WithLength(const IMPL_RTL_STRCODE*, sal_Int32, const IMPL_RTL_STRCODE*,
+ sal_Int32);
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 lastIndexOfStr ( const IMPL_RTL_STRCODE* pStr,
+ const IMPL_RTL_STRCODE* pSubStr )
+{
+ return lastIndexOfStr_WithLength( pStr, getLength( pStr ),
+ pSubStr, getLength( pSubStr ) );
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 lastIndexOfStr_WithLength ( const IMPL_RTL_STRCODE* pStr,
+ sal_Int32 nStrLen,
+ const IMPL_RTL_STRCODE* pSubStr,
+ sal_Int32 nSubLen )
+{
+ assert(nStrLen >= 0);
+ assert(nSubLen >= 0);
+ /* an empty SubString is always not findable */
+ if ( nSubLen == 0 )
+ return -1;
+ // take advantage of builtin optimisations
+ using my_string_view = std::basic_string_view<IMPL_RTL_STRCODE>;
+ my_string_view v(pStr, nStrLen);
+ my_string_view needle(pSubStr, nSubLen);
+ typename my_string_view::size_type idx = v.rfind(needle);
+ return idx == my_string_view::npos ? -1 : idx;
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <class S, class Replacer> void replaceChars(S str, Replacer replacer)
+{
+ for (auto& rChar : str)
+ rChar = replacer.Replace(rChar);
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE> sal_Int32 trim_WithLength(IMPL_RTL_STRCODE*, sal_Int32);
+
+template <typename IMPL_RTL_STRCODE> sal_Int32 trim( IMPL_RTL_STRCODE* pStr )
+{
+ return trim_WithLength( pStr, getLength( pStr ) );
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+std::basic_string_view<IMPL_RTL_STRCODE> trimView( IMPL_RTL_STRCODE* pStr, sal_Int32 nLen )
+{
+ assert(nLen >= 0);
+ sal_Int32 nPreSpaces = 0;
+ sal_Int32 nPostSpaces = 0;
+ sal_Int32 nIndex = nLen-1;
+
+ while ( (nPreSpaces < nLen) && implIsWhitespace( IMPL_RTL_USTRCODE(*(pStr+nPreSpaces)) ) )
+ nPreSpaces++;
+
+ while ( (nIndex > nPreSpaces) && implIsWhitespace( IMPL_RTL_USTRCODE(*(pStr+nIndex)) ) )
+ {
+ nPostSpaces++;
+ nIndex--;
+ }
+
+ return { pStr + nPreSpaces, static_cast<size_t>(nLen - nPostSpaces - nPreSpaces) };
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 trim_WithLength( IMPL_RTL_STRCODE* pStr, sal_Int32 nLen )
+{
+ const auto view = trimView(pStr, nLen);
+
+ if (static_cast<sal_Int32>(view.size()) != nLen)
+ {
+ nLen = static_cast<sal_Int32>(view.size());
+ if (view.data() != pStr)
+ Copy(pStr, view.data(), nLen);
+ *(pStr+nLen) = 0;
+ }
+
+ return nLen;
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE> sal_Int32 valueOfBoolean( IMPL_RTL_STRCODE* pStr, sal_Bool b )
+{
+ assert(pStr);
+ if ( b )
+ {
+ *pStr = 't';
+ pStr++;
+ *pStr = 'r';
+ pStr++;
+ *pStr = 'u';
+ pStr++;
+ *pStr = 'e';
+ pStr++;
+ *pStr = 0;
+ return 4;
+ }
+ else
+ {
+ *pStr = 'f';
+ pStr++;
+ *pStr = 'a';
+ pStr++;
+ *pStr = 'l';
+ pStr++;
+ *pStr = 's';
+ pStr++;
+ *pStr = 'e';
+ pStr++;
+ *pStr = 0;
+ return 5;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE>
+sal_Int32 valueOfChar ( IMPL_RTL_STRCODE* pStr,
+ IMPL_RTL_STRCODE c )
+{
+ assert(pStr);
+ *pStr++ = c;
+ *pStr = 0;
+ return 1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <sal_Int32 maxLen, typename IMPL_RTL_STRCODE, typename T>
+sal_Int32 valueOfInt ( IMPL_RTL_STRCODE* pStr,
+ T n,
+ sal_Int16 nRadix )
+{
+ assert(pStr);
+ assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX );
+ char aBuf[maxLen];
+ char* pBuf = aBuf;
+ sal_Int32 nLen = 0;
+ using uT = std::make_unsigned_t<T>;
+ uT nValue;
+
+ /* Radix must be valid */
+ if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) )
+ nRadix = 10;
+
+ if constexpr (std::is_signed_v<T>)
+ {
+ /* is value negative */
+ if ( n < 0 )
+ {
+ *pStr = '-';
+ pStr++;
+ nLen++;
+ nValue = n == std::numeric_limits<T>::min() ? static_cast<uT>(n) : -n;
+ }
+ else
+ nValue = n;
+ }
+ else
+ nValue = n;
+
+ /* create a recursive buffer with all values, except the last one */
+ do
+ {
+ char nDigit = static_cast<char>(nValue % nRadix);
+ nValue /= nRadix;
+ if ( nDigit > 9 )
+ *pBuf = (nDigit-10) + 'a';
+ else
+ *pBuf = (nDigit + '0' );
+ pBuf++;
+ }
+ while ( nValue > 0 );
+
+ /* copy the values in the right direction into the destination buffer */
+ do
+ {
+ pBuf--;
+ *pStr = *pBuf;
+ pStr++;
+ nLen++;
+ }
+ while ( pBuf != aBuf );
+ *pStr = 0;
+
+ return nLen;
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRCODE> sal_Bool toBoolean( const IMPL_RTL_STRCODE* pStr )
+{
+ assert(pStr);
+ if ( *pStr == '1' )
+ return true;
+
+ if ( (*pStr == 'T') || (*pStr == 't') )
+ {
+ pStr++;
+ if ( (*pStr == 'R') || (*pStr == 'r') )
+ {
+ pStr++;
+ if ( (*pStr == 'U') || (*pStr == 'u') )
+ {
+ pStr++;
+ if ( (*pStr == 'E') || (*pStr == 'e') )
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename T, class Iter> inline bool HandleSignChar(Iter& iter)
+{
+ if constexpr (std::numeric_limits<T>::is_signed)
+ {
+ if (*iter == '-')
+ {
+ ++iter;
+ return true;
+ }
+ }
+ if (*iter == '+')
+ ++iter;
+ return false;
+}
+
+template <typename T> std::pair<T, sal_Int16> DivMod(sal_Int16 nRadix, [[maybe_unused]] bool bNeg)
+{
+ if constexpr (std::numeric_limits<T>::is_signed)
+ if (bNeg)
+ return { -(std::numeric_limits<T>::min() / nRadix),
+ -(std::numeric_limits<T>::min() % nRadix) };
+ return { std::numeric_limits<T>::max() / nRadix, std::numeric_limits<T>::max() % nRadix };
+}
+
+template <typename T, class S> T toInt(S str, sal_Int16 nRadix)
+{
+ assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX );
+
+ if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) )
+ nRadix = 10;
+
+ auto pStr = str.begin();
+ const auto end = str.end();
+
+ /* Skip whitespaces */
+ while (pStr != end && implIsWhitespace(IMPL_RTL_USTRCODE(*pStr)))
+ pStr++;
+ if (pStr == end)
+ return 0;
+
+ const bool bNeg = HandleSignChar<T>(pStr);
+ const auto& [nDiv, nMod] = DivMod<T>(nRadix, bNeg);
+ assert(nDiv > 0);
+
+ std::make_unsigned_t<T> n = 0;
+ while (pStr != end)
+ {
+ sal_Int16 nDigit = implGetDigit(IMPL_RTL_USTRCODE(*pStr), nRadix);
+ if ( nDigit < 0 )
+ break;
+ if (static_cast<std::make_unsigned_t<T>>(nMod < nDigit ? nDiv - 1 : nDiv) < n)
+ return 0;
+
+ n *= nRadix;
+ n += nDigit;
+
+ pStr++;
+ }
+
+ if constexpr (std::numeric_limits<T>::is_signed)
+ if (bNeg)
+ return n == static_cast<std::make_unsigned_t<T>>(std::numeric_limits<T>::min())
+ ? std::numeric_limits<T>::min()
+ : -static_cast<T>(n);
+ return static_cast<T>(n);
+}
+
+/* ======================================================================= */
+/* Internal String-Class help functions */
+/* ======================================================================= */
+
+template <class STRINGDATA> using STRCODE = std::remove_extent_t<decltype(STRINGDATA::buffer)>;
+template <typename C> struct STRINGDATA_;
+template <> struct STRINGDATA_<char>
+{
+ using T = rtl_String;
+};
+template <> struct STRINGDATA_<sal_Unicode>
+{
+ using T = rtl_uString;
+};
+template <typename C> using STRINGDATA = typename STRINGDATA_<C>::T;
+
+template <typename IMPL_RTL_STRINGDATA> IMPL_RTL_STRINGDATA* Alloc( sal_Int32 nLen )
+{
+ IMPL_RTL_STRINGDATA * pData
+ = (sal::static_int_cast< sal_uInt32 >(nLen)
+ <= ((SAL_MAX_UINT32 - sizeof (IMPL_RTL_STRINGDATA))
+ / sizeof (STRCODE<IMPL_RTL_STRINGDATA>)))
+ ? static_cast<IMPL_RTL_STRINGDATA *>(rtl_allocateString(
+ sizeof (IMPL_RTL_STRINGDATA) + nLen * sizeof (STRCODE<IMPL_RTL_STRINGDATA>)))
+ : nullptr;
+ if (pData != nullptr) {
+ pData->refCount = 1;
+ pData->length = nLen;
+ pData->buffer[nLen] = 0;
+ }
+ return pData;
+}
+
+/* ======================================================================= */
+/* String-Class functions */
+/* ======================================================================= */
+
+template <typename IMPL_RTL_STRINGDATA> void acquire(IMPL_RTL_STRINGDATA * pThis)
+{
+ if (!SAL_STRING_IS_STATIC (pThis))
+ osl_atomic_increment( &((pThis)->refCount) );
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRINGDATA> void release( IMPL_RTL_STRINGDATA* pThis )
+{
+ if (SAL_UNLIKELY(SAL_STRING_IS_STATIC (pThis)))
+ return;
+
+ /* OString doesn't have an 'intern' */
+ if constexpr (sizeof(STRCODE<IMPL_RTL_STRINGDATA>) == sizeof(sal_Unicode))
+ {
+ if (SAL_STRING_IS_INTERN (pThis))
+ {
+ internRelease (pThis);
+ return;
+ }
+ }
+
+ if ( !osl_atomic_decrement( &(pThis->refCount) ) )
+ {
+ RTL_LOG_STRING_DELETE( pThis );
+ rtl_freeString( pThis );
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* static data to be referenced by all empty strings
+ * the refCount is predefined to 1 and must never become 0 !
+ */
+template <typename IMPL_RTL_STRINGDATA> struct EmptyStringImpl
+{
+ static IMPL_RTL_STRINGDATA data;
+};
+
+template <>
+inline rtl_uString EmptyStringImpl<rtl_uString>::data = {
+ sal_Int32(SAL_STRING_INTERN_FLAG | SAL_STRING_STATIC_FLAG | 1), /* sal_Int32 refCount; */
+ 0, /* sal_Int32 length; */
+ { 0 } /* sal_Unicode buffer[1]; */
+};
+
+template <>
+inline rtl_String EmptyStringImpl<rtl_String>::data = {
+ SAL_STRING_STATIC_FLAG | 1, /* sal_Int32 refCount; */
+ 0, /* sal_Int32 length; */
+ { 0 } /* char buffer[1]; */
+};
+
+template <typename IMPL_RTL_STRINGDATA> void new_( IMPL_RTL_STRINGDATA** ppThis )
+{
+ assert(ppThis);
+ if ( *ppThis)
+ release( *ppThis );
+
+ *ppThis = &EmptyStringImpl<IMPL_RTL_STRINGDATA>::data;
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRINGDATA>
+void new_WithLength( IMPL_RTL_STRINGDATA** ppThis, sal_Int32 nLen )
+{
+ assert(ppThis);
+ assert(nLen >= 0);
+ if ( nLen <= 0 )
+ new_( ppThis );
+ else
+ {
+ if ( *ppThis)
+ release( *ppThis );
+
+ *ppThis = Alloc<IMPL_RTL_STRINGDATA>( nLen );
+ assert(*ppThis != nullptr);
+ (*ppThis)->length = 0;
+ (*ppThis)->buffer[0] = 0;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRINGDATA, typename C>
+void newFromStr_WithLength(IMPL_RTL_STRINGDATA** ppThis, const C* pCharStr, sal_Int32 nLen,
+ sal_Int32 allocExtra = 0)
+{
+ assert(ppThis);
+ assert(nLen >= 0);
+ assert(pCharStr || nLen == 0);
+ assert(allocExtra >= 0);
+
+ if (nLen + allocExtra == 0)
+ return new_(ppThis);
+
+ IMPL_RTL_STRINGDATA* pOrg = *ppThis;
+ *ppThis = Alloc<IMPL_RTL_STRINGDATA>(nLen + allocExtra);
+ assert(*ppThis != nullptr);
+ if (nLen > 0)
+ Copy((*ppThis)->buffer, pCharStr, nLen);
+ if (allocExtra > 0)
+ {
+ (*ppThis)->length = nLen;
+ (*ppThis)->buffer[nLen] = 0;
+ }
+
+ RTL_LOG_STRING_NEW(*ppThis);
+
+ /* must be done last, if pCharStr belongs to *ppThis */
+ if (pOrg)
+ release(pOrg);
+}
+
+template <typename IMPL_RTL_STRINGDATA>
+void newFromString ( IMPL_RTL_STRINGDATA** ppThis,
+ const IMPL_RTL_STRINGDATA* pStr )
+{
+ assert(pStr);
+
+ newFromStr_WithLength(ppThis, pStr->buffer, pStr->length);
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRINGDATA>
+void newFromStr ( IMPL_RTL_STRINGDATA** ppThis,
+ const STRCODE<IMPL_RTL_STRINGDATA>* pCharStr )
+{
+#if OSL_DEBUG_LEVEL > 0
+ //TODO: For now, only abort in non-production debug builds; once all places that rely on the
+ // undocumented newFromStr behavior of treating a null pCharStr like an empty string have been
+ // found and fixed, drop support for that behavior and turn this into a general assert:
+ if (pCharStr == nullptr) {
+ std::abort();
+ }
+#endif
+
+ newFromStr_WithLength(ppThis, pCharStr, pCharStr ? getLength(pCharStr) : 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRINGDATA> void assign(IMPL_RTL_STRINGDATA**, IMPL_RTL_STRINGDATA*);
+
+template <typename IMPL_RTL_STRINGDATA>
+void newFromSubString ( IMPL_RTL_STRINGDATA** ppThis,
+ const IMPL_RTL_STRINGDATA* pFrom,
+ sal_Int32 beginIndex,
+ sal_Int32 count )
+{
+ assert(ppThis);
+ if ( beginIndex == 0 && count == pFrom->length )
+ return assign(ppThis, const_cast<IMPL_RTL_STRINGDATA*>(pFrom));
+ if ( count < 0 || beginIndex < 0 || beginIndex + count > pFrom->length )
+ {
+ assert(false); // fail fast at least in debug builds
+ return newFromStr_WithLength(ppThis, "!!br0ken!!", 10);
+ }
+
+ newFromStr_WithLength( ppThis, pFrom->buffer + beginIndex, count );
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRINGDATA>
+void assign ( IMPL_RTL_STRINGDATA** ppThis,
+ IMPL_RTL_STRINGDATA* pStr )
+{
+ assert(ppThis);
+ /* must be done at first, if pStr == *ppThis */
+ acquire( pStr );
+
+ if ( *ppThis )
+ release( *ppThis );
+
+ *ppThis = pStr;
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRINGDATA> auto* getStr( IMPL_RTL_STRINGDATA* pThis )
+{
+ assert(pThis);
+ return pThis->buffer;
+}
+
+/* ----------------------------------------------------------------------- */
+
+enum ThrowPolicy { NoThrow, Throw };
+
+template <ThrowPolicy throwPolicy, typename IMPL_RTL_STRINGDATA, typename C1, typename C2>
+void newConcat(IMPL_RTL_STRINGDATA** ppThis, const C1* pLeft, sal_Int32 nLeftLength,
+ const C2* pRight, sal_Int32 nRightLength)
+{
+ assert(ppThis);
+ assert(nLeftLength >= 0);
+ assert(pLeft || nLeftLength == 0);
+ assert(nRightLength >= 0);
+ assert(pRight || nRightLength == 0);
+ IMPL_RTL_STRINGDATA* pOrg = *ppThis;
+
+ if (nLeftLength > std::numeric_limits<sal_Int32>::max() - nRightLength)
+ {
+ if constexpr (throwPolicy == NoThrow)
+ *ppThis = nullptr;
+ else
+ {
+#if !defined(__COVERITY__)
+ throw std::length_error("newConcat");
+#else
+ //coverity doesn't report std::bad_alloc as an unhandled exception when
+ //potentially thrown from destructors but does report std::length_error
+ throw std::bad_alloc();
+#endif
+ }
+ }
+ else
+ {
+ auto* pTempStr = Alloc<IMPL_RTL_STRINGDATA>(nLeftLength + nRightLength);
+ OSL_ASSERT(pTempStr != nullptr);
+ *ppThis = pTempStr;
+ if (*ppThis != nullptr) {
+ if (nLeftLength)
+ Copy( pTempStr->buffer, pLeft, nLeftLength );
+ if (nRightLength)
+ Copy( pTempStr->buffer+nLeftLength, pRight, nRightLength );
+
+ RTL_LOG_STRING_NEW( *ppThis );
+ }
+ }
+
+ /* must be done last, if left or right == *ppThis */
+ if ( pOrg )
+ release( pOrg );
+}
+
+template<typename IMPL_RTL_STRINGDATA, typename IMPL_RTL_STRCODE>
+void newConcat(IMPL_RTL_STRINGDATA** ppThis, IMPL_RTL_STRINGDATA* pLeft,
+ const IMPL_RTL_STRCODE* pRight, sal_Int32 nRightLength)
+{
+ assert(pLeft != nullptr);
+ if (nRightLength == 0)
+ assign(ppThis, pLeft);
+ else
+ newConcat<Throw>(ppThis, pLeft->buffer, pLeft->length, pRight, nRightLength);
+}
+
+template <typename IMPL_RTL_STRINGDATA>
+void newConcat ( IMPL_RTL_STRINGDATA** ppThis,
+ IMPL_RTL_STRINGDATA* pLeft,
+ IMPL_RTL_STRINGDATA* pRight )
+{
+ /* Test for 0-Pointer - if not, change newReplaceStrAt! */
+ if ( !pRight || !pRight->length )
+ {
+ assert(pLeft != nullptr);
+ assign(ppThis, pLeft);
+ }
+ else if ( !pLeft || !pLeft->length )
+ assign(ppThis, pRight);
+ else
+ newConcat<NoThrow>(ppThis, pLeft->buffer, pLeft->length, pRight->buffer, pRight->length);
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRINGDATA>
+void ensureCapacity ( IMPL_RTL_STRINGDATA** ppThis,
+ sal_Int32 size )
+{
+ assert(ppThis);
+ IMPL_RTL_STRINGDATA* const pOrg = *ppThis;
+ if ( pOrg->refCount == 1 && pOrg->length >= size )
+ return;
+ assert( pOrg->length <= size ); // do not truncate
+ auto* pTempStr = Alloc<IMPL_RTL_STRINGDATA>( size );
+ Copy( pTempStr->buffer, pOrg->buffer, pOrg->length );
+ // right now the length is still the same as of the original
+ pTempStr->length = pOrg->length;
+ pTempStr->buffer[ pOrg->length ] = '\0';
+ *ppThis = pTempStr;
+ RTL_LOG_STRING_NEW( *ppThis );
+
+ release( pOrg );
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRINGDATA, typename IMPL_RTL_STRCODE>
+void newReplaceStrAt ( IMPL_RTL_STRINGDATA** ppThis,
+ IMPL_RTL_STRINGDATA* pStr,
+ sal_Int32 nIndex,
+ sal_Int32 nCount,
+ const IMPL_RTL_STRCODE* pNewSubStr,
+ sal_Int32 nNewSubStrLen )
+{
+ assert(ppThis);
+ assert(nIndex >= 0 && nIndex <= pStr->length);
+ assert(nCount >= 0);
+ assert(nCount <= pStr->length - nIndex);
+ assert(pNewSubStr != nullptr || nNewSubStrLen == 0);
+ assert(nNewSubStrLen >= 0);
+ /* Append? */
+ if ( nIndex >= pStr->length )
+ return newConcat(ppThis, pStr, pNewSubStr, nNewSubStrLen);
+
+ /* not more than the String length could be deleted */
+ if ( nCount >= pStr->length-nIndex )
+ {
+ /* Assign of NewSubStr? */
+ if (nIndex == 0)
+ return newFromStr_WithLength( ppThis, pNewSubStr, nNewSubStrLen );
+
+ nCount = pStr->length - nIndex;
+ }
+
+ /* Assign of Str? */
+ if ( !nCount && !nNewSubStrLen )
+ return assign(ppThis, pStr);
+
+ IMPL_RTL_STRINGDATA* pOrg = *ppThis;
+
+ /* Alloc New Buffer */
+ *ppThis = Alloc<IMPL_RTL_STRINGDATA>(pStr->length - nCount + nNewSubStrLen);
+ assert(*ppThis != nullptr);
+ auto* pBuffer = (*ppThis)->buffer;
+ if ( nIndex )
+ {
+ Copy( pBuffer, pStr->buffer, nIndex );
+ pBuffer += nIndex;
+ }
+ if ( nNewSubStrLen )
+ {
+ Copy( pBuffer, pNewSubStr, nNewSubStrLen );
+ pBuffer += nNewSubStrLen;
+ }
+ Copy( pBuffer, pStr->buffer+nIndex+nCount, pStr->length-nIndex-nCount );
+
+ RTL_LOG_STRING_NEW( *ppThis );
+ /* must be done last, if pStr or pNewSubStr == *ppThis */
+ if ( pOrg )
+ release( pOrg );
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRINGDATA>
+void newReplaceStrAt ( IMPL_RTL_STRINGDATA** ppThis,
+ IMPL_RTL_STRINGDATA* pStr,
+ sal_Int32 nIndex,
+ sal_Int32 nCount,
+ IMPL_RTL_STRINGDATA* pNewSubStr )
+{
+ assert(ppThis);
+ assert(nIndex >= 0 && nIndex <= pStr->length);
+ assert(nCount >= 0);
+ assert(nCount <= pStr->length - nIndex);
+ /* Append? */
+ if (nIndex >= pStr->length)
+ {
+ /* newConcat test, if pNewSubStr is 0 */
+ newConcat(ppThis, pStr, pNewSubStr);
+ return;
+ }
+
+ /* not more than the String length could be deleted */
+ if (nCount >= pStr->length-nIndex)
+ {
+ /* Assign of NewSubStr? */
+ if (nIndex == 0)
+ {
+ if (!pNewSubStr)
+ return new_(ppThis);
+ else
+ return assign(ppThis, pNewSubStr);
+ }
+ nCount = pStr->length - nIndex;
+ }
+
+ /* Assign of Str? */
+ if (!nCount && (!pNewSubStr || !pNewSubStr->length))
+ {
+ assign(ppThis, pStr);
+ return;
+ }
+
+ const auto* pNewSubStrBuf = pNewSubStr ? pNewSubStr->buffer : nullptr;
+ const sal_Int32 nNewSubStrLength = pNewSubStr ? pNewSubStr->length : 0;
+ newReplaceStrAt(ppThis, pStr, nIndex, nCount, pNewSubStrBuf, nNewSubStrLength);
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <class Traits, typename IMPL_RTL_STRINGDATA>
+void newReplaceChars(IMPL_RTL_STRINGDATA** ppThis, IMPL_RTL_STRINGDATA* pStr)
+{
+ assert(ppThis);
+ assert(pStr);
+
+ const auto pEnd = pStr->buffer + pStr->length;
+ auto pCharStr = std::find_if(pStr->buffer, pEnd, [](auto c) { return Traits::Applicable(c); });
+ if (pCharStr != pEnd)
+ {
+ IMPL_RTL_STRINGDATA* pOrg = *ppThis;
+ *ppThis = Alloc<IMPL_RTL_STRINGDATA>(pStr->length);
+ assert(*ppThis != nullptr);
+ auto* pNewCharStr = (*ppThis)->buffer;
+ /* Copy String */
+ const sal_Int32 nCount = pCharStr - pStr->buffer;
+ Copy(pNewCharStr, pStr->buffer, nCount);
+ pNewCharStr += nCount;
+ /* replace/copy rest of the string */
+ do
+ {
+ *pNewCharStr = Traits::Replace(*pCharStr);
+ pNewCharStr++;
+ pCharStr++;
+ } while (pCharStr != pEnd);
+
+ RTL_LOG_STRING_NEW(*ppThis);
+ /* must be done last, if pStr == *ppThis */
+ if (pOrg)
+ release(pOrg);
+ }
+ else
+ assign(ppThis, pStr);
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRINGDATA>
+void newTrim ( IMPL_RTL_STRINGDATA** ppThis,
+ IMPL_RTL_STRINGDATA* pStr )
+{
+ assert(pStr);
+ const auto view = trimView(pStr->buffer, pStr->length);
+
+ if (static_cast<sal_Int32>(view.size()) == pStr->length)
+ assign(ppThis, pStr);
+ else
+ newFromStr_WithLength(ppThis, view.data(), view.size());
+}
+
+/* ----------------------------------------------------------------------- */
+
+template <typename IMPL_RTL_STRINGDATA>
+sal_Int32 getToken ( IMPL_RTL_STRINGDATA** ppThis,
+ IMPL_RTL_STRINGDATA* pStr,
+ sal_Int32 nToken,
+ STRCODE<IMPL_RTL_STRINGDATA> cTok,
+ sal_Int32 nIndex )
+{
+ assert(ppThis);
+ assert(pStr);
+ assert(nIndex <= pStr->length);
+
+ // Set ppThis to an empty string and return -1 if either nToken or nIndex is
+ // negative:
+ if (nIndex >= 0 && nToken >= 0)
+ {
+ const auto* pOrgCharStr = pStr->buffer;
+ const auto* pCharStr = pOrgCharStr + nIndex;
+ sal_Int32 nLen = pStr->length - nIndex;
+ sal_Int32 nTokCount = 0;
+ const auto* pCharStrStart = pCharStr;
+ while (nLen > 0)
+ {
+ if (*pCharStr == cTok)
+ {
+ nTokCount++;
+
+ if (nTokCount > nToken)
+ break;
+ if (nTokCount == nToken)
+ pCharStrStart = pCharStr + 1;
+ }
+
+ pCharStr++;
+ nLen--;
+ }
+ if (nTokCount >= nToken)
+ {
+ newFromStr_WithLength(ppThis, pCharStrStart, pCharStr - pCharStrStart);
+ if (nLen > 0)
+ return pCharStr - pOrgCharStr + 1;
+ else
+ return -1;
+ }
+ }
+
+ new_(ppThis);
+ return -1;
+}
+
+/* ======================================================================= */
+/* String buffer help functions */
+/* ======================================================================= */
+
+template <class IMPL_RTL_STRINGDATA>
+void stringbuffer_newFromStr_WithLength(IMPL_RTL_STRINGDATA** ppThis,
+ const STRCODE<IMPL_RTL_STRINGDATA>* pStr, sal_Int32 count)
+{
+ assert(ppThis);
+ assert(count >= 0);
+ if (!pStr)
+ count = 0; // Because old code didn't care about count when !pStr
+
+ newFromStr_WithLength(ppThis, pStr, count, 16);
+}
+
+template <class IMPL_RTL_STRINGDATA>
+sal_Int32 stringbuffer_newFromStringBuffer(IMPL_RTL_STRINGDATA** ppThis, sal_Int32 capacity,
+ IMPL_RTL_STRINGDATA* pStr)
+{
+ assert(capacity >= 0);
+ assert(pStr);
+
+ if (capacity < pStr->length)
+ capacity = pStr->length;
+
+ newFromStr_WithLength(ppThis, pStr->buffer, pStr->length, capacity - pStr->length);
+ return capacity;
+}
+
+template <class IMPL_RTL_STRINGDATA>
+void stringbuffer_ensureCapacity(IMPL_RTL_STRINGDATA** ppThis, sal_Int32* capacity,
+ sal_Int32 minimumCapacity)
+{
+ assert(ppThis);
+ assert(capacity && *capacity >= 0);
+ // assert(minimumCapacity >= 0); // It was commented out in rtl_stringbuffer_ensureCapacity
+ if (minimumCapacity <= *capacity)
+ return;
+
+ const auto nLength = (*ppThis)->length;
+ *capacity = (nLength + 1) * 2;
+ if (minimumCapacity > *capacity)
+ *capacity = minimumCapacity;
+
+ newFromStr_WithLength(ppThis, (*ppThis)->buffer, nLength, *capacity - nLength);
+}
+
+template <class IMPL_RTL_STRINGDATA, typename C>
+void stringbuffer_insert(IMPL_RTL_STRINGDATA** ppThis, sal_Int32* capacity, sal_Int32 offset,
+ const C* pStr, sal_Int32 len)
+{
+ assert(ppThis);
+ assert(capacity && *capacity >= 0);
+ assert(offset >= 0 && offset <= (*ppThis)->length);
+ assert(len >= 0);
+ if (len == 0)
+ return;
+
+ stringbuffer_ensureCapacity(ppThis, capacity, (*ppThis)->length + len);
+
+ sal_Int32 nOldLen = (*ppThis)->length;
+ auto* pBuf = (*ppThis)->buffer;
+
+ /* copy the tail */
+ const sal_Int32 n = nOldLen - offset;
+ if (n > 0)
+ CopyBackward(pBuf + offset + len, pBuf + offset, n);
+
+ /* insert the new characters */
+ if (pStr != nullptr)
+ Copy(pBuf + offset, pStr, len);
+
+ (*ppThis)->length = nOldLen + len;
+ pBuf[nOldLen + len] = 0;
+}
+
+template <class IMPL_RTL_STRINGDATA>
+void stringbuffer_remove(IMPL_RTL_STRINGDATA** ppThis, sal_Int32 start, sal_Int32 len)
+{
+ assert(ppThis);
+ assert(start >= 0 && start <= (*ppThis)->length);
+ assert(len >= 0);
+
+ if (len > (*ppThis)->length - start)
+ len = (*ppThis)->length - start;
+
+ //remove nothing
+ if (!len)
+ return;
+
+ auto* pBuf = (*ppThis)->buffer;
+ const sal_Int32 nTailLen = (*ppThis)->length - (start + len);
+
+ if (nTailLen)
+ {
+ /* move the tail */
+ Copy(pBuf + start, pBuf + start + len, nTailLen);
+ }
+
+ (*ppThis)->length -= len;
+ pBuf[(*ppThis)->length] = 0;
+}
+
+template <class S, typename CharTypeFrom, typename CharTypeTo>
+void newReplaceAllFromIndex(S** s, S* s1, CharTypeFrom const* from, sal_Int32 fromLength,
+ CharTypeTo const* to, sal_Int32 toLength, sal_Int32 fromIndex)
+{
+ assert(s != nullptr);
+ assert(s1 != nullptr);
+ assert(fromLength >= 0);
+ assert(from != nullptr || fromLength == 0);
+ assert(toLength >= 0);
+ assert(to != nullptr || toLength == 0);
+ assert(fromIndex >= 0 && fromIndex <= s1->length);
+ sal_Int32 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex,
+ from, fromLength);
+ if (i >= 0)
+ {
+ if (s1->length - fromLength > SAL_MAX_INT32 - toLength)
+ std::abort();
+ i += fromIndex;
+ sal_Int32 nCapacity = s1->length + (toLength - fromLength);
+ if (fromLength < toLength)
+ {
+ // Pre-allocate up to 16 replacements more
+ const sal_Int32 nMaxMoreFinds = (s1->length - i - fromLength) / fromLength;
+ const sal_Int32 nIncrease = toLength - fromLength;
+ const sal_Int32 nMoreReplacements = std::min(
+ { nMaxMoreFinds, (SAL_MAX_INT32 - nCapacity) / nIncrease, sal_Int32(16) });
+ nCapacity += nMoreReplacements * nIncrease;
+ }
+ const auto pOld = *s;
+ *s = Alloc<S>(nCapacity);
+ (*s)->length = 0;
+ fromIndex = 0;
+ do
+ {
+ stringbuffer_insert(s, &nCapacity, (*s)->length, s1->buffer + fromIndex, i);
+ stringbuffer_insert(s, &nCapacity, (*s)->length, to, toLength);
+ fromIndex += i + fromLength;
+ i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex,
+ from, fromLength);
+ } while (i >= 0);
+ // the rest
+ stringbuffer_insert(s, &nCapacity, (*s)->length,
+ s1->buffer + fromIndex, s1->length - fromIndex);
+ if (pOld)
+ release(pOld); // Must be last in case *s == s1
+ }
+ else
+ assign(s, s1);
+
+ RTL_LOG_STRING_NEW(*s);
+}
+
+template <typename IMPL_RTL_STRINGDATA>
+void newReplace(IMPL_RTL_STRINGDATA** ppThis, IMPL_RTL_STRINGDATA* pStr,
+ STRCODE<IMPL_RTL_STRINGDATA> cOld, STRCODE<IMPL_RTL_STRINGDATA> cNew)
+{
+ return newReplaceAllFromIndex(ppThis, pStr, &cOld, 1, &cNew, 1, 0);
+}
+
+template <class S, typename CharTypeFrom, typename CharTypeTo>
+void newReplaceFirst(S** s, S* s1, CharTypeFrom const* from, sal_Int32 fromLength,
+ CharTypeTo const* to, sal_Int32 toLength, sal_Int32& fromIndex)
+{
+ assert(s != nullptr);
+ assert(s1 != nullptr);
+ assert(fromLength >= 0);
+ assert(from != nullptr || fromLength == 0);
+ assert(toLength >= 0);
+ assert(to != nullptr || toLength == 0);
+ assert(fromIndex >= 0 && fromIndex <= s1->length);
+ sal_Int32 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex,
+ from, fromLength);
+ if (i >= 0)
+ {
+ if (s1->length - fromLength > SAL_MAX_INT32 - toLength)
+ std::abort();
+ i += fromIndex;
+ newReplaceStrAt(s, s1, i, fromLength, to, toLength);
+ }
+ else
+ assign(s, s1);
+
+ fromIndex = i;
+}
+
+// doubleToString implementation
+
+static inline constexpr sal_uInt64 eX[] = { 10ull,
+ 100ull,
+ 1000ull,
+ 10000ull,
+ 100000ull,
+ 1000000ull,
+ 10000000ull,
+ 100000000ull,
+ 1000000000ull,
+ 10000000000ull,
+ 100000000000ull,
+ 1000000000000ull,
+ 10000000000000ull,
+ 100000000000000ull,
+ 1000000000000000ull,
+ 10000000000000000ull,
+ 100000000000000000ull,
+ 1000000000000000000ull,
+ 10000000000000000000ull };
+
+template <typename S>
+void doubleToString(S** pResult, sal_Int32* pResultCapacity, sal_Int32 nResultOffset, double fValue,
+ rtl_math_StringFormat eFormat, sal_Int32 nDecPlaces, STRCODE<S> cDecSeparator,
+ sal_Int32 const* pGroups, STRCODE<S> cGroupSeparator,
+ bool bEraseTrailingDecZeros)
+{
+ auto decimalDigits = [](sal_uInt64 n) {
+ return std::distance(std::begin(eX), std::upper_bound(std::begin(eX), std::end(eX), n)) + 1;
+ };
+
+ auto roundToPow10 = [](sal_uInt64 n, int e) {
+ assert(e > 0 && o3tl::make_unsigned(e) <= std::size(eX));
+ const sal_uInt64 d = eX[e - 1];
+ return (n + d / 2) / d * d;
+ };
+
+ auto append = [](S** s, sal_Int32* pCapacity, sal_Int32& rOffset, auto sv)
+ {
+ stringbuffer_insert(s, pCapacity, rOffset, sv.data(), sv.size());
+ rOffset += sv.size();
+ };
+
+ if (std::isnan(fValue))
+ {
+ // #i112652# XMLSchema-2
+ constexpr std::string_view nan{ "NaN" };
+ if (!pResultCapacity)
+ return newFromStr_WithLength(pResult, nan.data(), nan.size());
+ else
+ return append(pResult, pResultCapacity, nResultOffset, nan);
+ }
+
+ // sign adjustment, instead of testing for fValue<0.0 this will also fetch -0.0
+ bool bSign = std::signbit(fValue);
+
+ if (std::isinf(fValue))
+ {
+ // #i112652# XMLSchema-2
+ std::string_view inf = bSign ? std::string_view("-INF") : std::string_view("INF");
+ if (!pResultCapacity)
+ return newFromStr_WithLength(pResult, inf.data(), inf.size());
+ else
+ return append(pResult, pResultCapacity, nResultOffset, inf);
+ }
+
+ if (bSign)
+ fValue = -fValue;
+
+ decltype(jkj::dragonbox::to_decimal(fValue, jkj::dragonbox::policy::sign::ignore,
+ jkj::dragonbox::policy::trailing_zero::ignore)) aParts{};
+ if (fValue) // to_decimal is documented to only handle non-zero finite numbers
+ aParts = jkj::dragonbox::to_decimal(fValue, jkj::dragonbox::policy::sign::ignore,
+ jkj::dragonbox::policy::trailing_zero::ignore);
+
+ int nOrigDigits = decimalDigits(aParts.significand);
+ int nExp = nOrigDigits + aParts.exponent - 1;
+ int nRoundDigits = 15;
+
+ // Unfortunately the old rounding below writes 1.79769313486232e+308 for
+ // DBL_MAX and 4 subsequent nextafter(...,0).
+ static const double fB1 = std::nextafter(std::numeric_limits<double>::max(), 0);
+ static const double fB2 = std::nextafter(fB1, 0);
+ static const double fB3 = std::nextafter(fB2, 0);
+ static const double fB4 = std::nextafter(fB3, 0);
+ if ((fValue >= fB4) && eFormat != rtl_math_StringFormat_F)
+ {
+ // 1.7976931348623157e+308 instead of rounded 1.79769313486232e+308
+ // that can't be converted back as out of range. For rounded values if
+ // they exceed range they should not be written to exchange strings or
+ // file formats.
+
+ eFormat = rtl_math_StringFormat_E;
+ nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, 0, 16);
+ nRoundDigits = 17;
+ }
+
+ // Use integer representation for integer values that fit into the
+ // mantissa (1.((2^53)-1)) with a precision of 1 for highest accuracy.
+ if ((eFormat == rtl_math_StringFormat_Automatic || eFormat == rtl_math_StringFormat_F)
+ && aParts.exponent >= 0 && fValue < 0x1p53)
+ {
+ eFormat = rtl_math_StringFormat_F;
+ if (nDecPlaces == rtl_math_DecimalPlaces_Max)
+ nDecPlaces = 0;
+ else
+ nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, -15, 15);
+
+ if (bEraseTrailingDecZeros && nDecPlaces > 0)
+ nDecPlaces = 0;
+
+ nRoundDigits = nOrigDigits; // no rounding
+ }
+
+ switch (eFormat)
+ {
+ case rtl_math_StringFormat_Automatic:
+ // E or F depending on exponent magnitude
+ if (nExp <= -15 || nExp >= 15)
+ {
+ if (nDecPlaces == rtl_math_DecimalPlaces_Max)
+ nDecPlaces = 14;
+ eFormat = rtl_math_StringFormat_E;
+ }
+ else
+ {
+ if (nDecPlaces == rtl_math_DecimalPlaces_Max)
+ nDecPlaces = (nExp < 14) ? 15 - nExp - 1 : 15;
+ eFormat = rtl_math_StringFormat_F;
+ }
+ break;
+
+ case rtl_math_StringFormat_G:
+ case rtl_math_StringFormat_G1:
+ case rtl_math_StringFormat_G2:
+ // G-Point, similar to sprintf %G
+ if (nDecPlaces == rtl_math_DecimalPlaces_DefaultSignificance)
+ nDecPlaces = 6;
+
+ if (nExp < -4 || nExp >= nDecPlaces)
+ {
+ nDecPlaces = std::max<sal_Int32>(1, nDecPlaces - 1);
+
+ if (eFormat == rtl_math_StringFormat_G)
+ eFormat = rtl_math_StringFormat_E;
+ else if (eFormat == rtl_math_StringFormat_G2)
+ eFormat = rtl_math_StringFormat_E2;
+ else if (eFormat == rtl_math_StringFormat_G1)
+ eFormat = rtl_math_StringFormat_E1;
+ }
+ else
+ {
+ nDecPlaces = std::max<sal_Int32>(0, nDecPlaces - nExp - 1);
+ eFormat = rtl_math_StringFormat_F;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Too large values for nDecPlaces make no sense; it might also be
+ // rtl_math_DecimalPlaces_Max was passed with rtl_math_StringFormat_F or
+ // others, but we don't want to allocate/deallocate 2GB just to fill it
+ // with trailing '0' characters..
+ nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, -20, 20);
+
+ sal_Int32 nDigits = nDecPlaces + 1;
+
+ if (eFormat == rtl_math_StringFormat_F)
+ nDigits += nExp;
+
+ // Round the number
+ nRoundDigits = std::min<int>(nDigits, nRoundDigits);
+ if (nDigits >= 0 && nOrigDigits > nRoundDigits)
+ {
+ aParts.significand = roundToPow10(aParts.significand, nOrigDigits - nRoundDigits);
+ assert(aParts.significand <= eX[nOrigDigits - 1]);
+ if (aParts.significand == eX[nOrigDigits - 1]) // up-rounding to the next decade
+ {
+ nOrigDigits++;
+ nExp++;
+
+ if (eFormat == rtl_math_StringFormat_F)
+ nDigits++;
+ }
+ }
+
+ sal_Int32 nBuf
+ = (nDigits <= 0 ? std::max<sal_Int32>(nDecPlaces, std::abs(nExp)) : nDigits + nDecPlaces)
+ + 10 + (pGroups ? std::abs(nDigits) * 2 : 0);
+ // max(nDigits) = max(nDecPlaces) + 1 + max(nExp) + 1 = 20 + 1 + 308 + 1 = 330
+ // max(nBuf) = max(nDigits) + max(nDecPlaces) + 10 + max(nDigits) * 2 = 330 * 3 + 20 + 10 = 1020
+ assert(nBuf <= 1024);
+ STRCODE<S>* const pBuf = static_cast<STRCODE<S>*>(alloca(nBuf * sizeof(STRCODE<S>)));
+ STRCODE<S>* p = pBuf;
+ if (bSign)
+ *p++ = '-';
+
+ bool bHasDec = false;
+
+ int nDecPos;
+ // Check for F format and number < 1
+ if (eFormat == rtl_math_StringFormat_F)
+ {
+ if (nExp < 0)
+ {
+ *p++ = '0';
+ if (nDecPlaces > 0)
+ {
+ *p++ = cDecSeparator;
+ bHasDec = true;
+ }
+
+ sal_Int32 i = (nDigits <= 0 ? nDecPlaces : -nExp - 1);
+
+ while ((i--) > 0)
+ *p++ = '0';
+
+ nDecPos = 0;
+ }
+ else
+ nDecPos = nExp + 1;
+ }
+ else
+ nDecPos = 1;
+
+ int nGrouping = 0, nGroupSelector = 0, nGroupExceed = 0;
+ if (nDecPos > 1 && pGroups && pGroups[0] && cGroupSeparator)
+ {
+ while (nGrouping + pGroups[nGroupSelector] < nDecPos)
+ {
+ nGrouping += pGroups[nGroupSelector];
+ if (pGroups[nGroupSelector + 1])
+ {
+ if (nGrouping + pGroups[nGroupSelector + 1] >= nDecPos)
+ break; // while
+
+ ++nGroupSelector;
+ }
+ else if (!nGroupExceed)
+ nGroupExceed = nGrouping;
+ }
+ }
+
+ // print the number
+ if (nDigits > 0)
+ {
+ for (int nCurExp = nOrigDigits - 1;;)
+ {
+ int nDigit;
+ if (aParts.significand > 0 && nCurExp > 0)
+ {
+ --nCurExp;
+ nDigit = aParts.significand / eX[nCurExp];
+ aParts.significand %= eX[nCurExp];
+ }
+ else
+ {
+ nDigit = aParts.significand;
+ aParts.significand = 0;
+ }
+ assert(nDigit >= 0 && nDigit < 10);
+ *p++ = nDigit + '0';
+
+ if (!--nDigits)
+ break; // for
+
+ if (nDecPos)
+ {
+ if (!--nDecPos)
+ {
+ *p++ = cDecSeparator;
+ bHasDec = true;
+ }
+ else if (nDecPos == nGrouping)
+ {
+ *p++ = cGroupSeparator;
+ nGrouping -= pGroups[nGroupSelector];
+
+ if (nGroupSelector && nGrouping < nGroupExceed)
+ --nGroupSelector;
+ }
+ }
+ }
+ }
+
+ if (!bHasDec && eFormat == rtl_math_StringFormat_F)
+ { // nDecPlaces < 0 did round the value
+ while (--nDecPos > 0)
+ { // fill before decimal point
+ if (nDecPos == nGrouping)
+ {
+ *p++ = cGroupSeparator;
+ nGrouping -= pGroups[nGroupSelector];
+
+ if (nGroupSelector && nGrouping < nGroupExceed)
+ --nGroupSelector;
+ }
+
+ *p++ = '0';
+ }
+ }
+
+ if (bEraseTrailingDecZeros && bHasDec)
+ {
+ while (*(p - 1) == '0')
+ p--;
+
+ if (*(p - 1) == cDecSeparator)
+ p--;
+ }
+
+ // Print the exponent ('E', followed by '+' or '-', followed by exactly
+ // three digits for rtl_math_StringFormat_E). The code in
+ // rtl_[u]str_valueOf{Float|Double} relies on this format.
+ if (eFormat == rtl_math_StringFormat_E || eFormat == rtl_math_StringFormat_E2
+ || eFormat == rtl_math_StringFormat_E1)
+ {
+ if (p == pBuf)
+ *p++ = '1';
+ // maybe no nDigits if nDecPlaces < 0
+
+ *p++ = 'E';
+ if (nExp < 0)
+ {
+ nExp = -nExp;
+ *p++ = '-';
+ }
+ else
+ *p++ = '+';
+
+ if (eFormat == rtl_math_StringFormat_E || nExp >= 100)
+ *p++ = nExp / 100 + '0';
+
+ nExp %= 100;
+
+ if (eFormat == rtl_math_StringFormat_E || eFormat == rtl_math_StringFormat_E2 || nExp >= 10)
+ *p++ = nExp / 10 + '0';
+
+ *p++ = nExp % 10 + '0';
+ }
+
+ if (!pResultCapacity)
+ newFromStr_WithLength(pResult, pBuf, p - pBuf);
+ else
+ append(pResult, pResultCapacity, nResultOffset, std::basic_string_view(pBuf, p - pBuf));
+}
+
+template <sal_Int32 maxLen, typename C, typename T> sal_Int32 SAL_CALL valueOfFP(C* pStr, T f)
+{
+ assert(pStr);
+ STRINGDATA<C>* pResult = nullptr;
+ doubleToString(&pResult, nullptr, 0, f, rtl_math_StringFormat_G,
+ maxLen - std::size("-x.E-xxx") + 1, '.', nullptr, 0, true);
+ const sal_Int32 nLen = pResult->length;
+ assert(nLen < maxLen);
+ Copy(pStr, pResult->buffer, nLen + 1);
+ release(pResult);
+ return nLen;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/unload.cxx b/sal/rtl/unload.cxx
new file mode 100644
index 000000000..beac56ffa
--- /dev/null
+++ b/sal/rtl/unload.cxx
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <osl/time.h>
+#include <rtl/unload.h>
+
+extern "C" void rtl_moduleCount_acquire(rtl_ModuleCount*) {}
+
+extern "C" void rtl_moduleCount_release(rtl_ModuleCount*) {}
+
+extern "C" sal_Bool rtl_moduleCount_canUnload(rtl_StandardModuleCount*, TimeValue*)
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/uri.cxx b/sal/rtl/uri.cxx
new file mode 100644
index 000000000..341455261
--- /dev/null
+++ b/sal/rtl/uri.cxx
@@ -0,0 +1,759 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/character.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/textenc.h>
+#include <rtl/textcvt.h>
+#include <rtl/uri.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <sal/macros.h>
+
+#include <uri_internal.hxx>
+
+#include <algorithm>
+#include <cstddef>
+
+namespace {
+
+sal_Unicode const cEscapePrefix = 0x25; // '%'
+
+int getHexWeight(sal_uInt32 nUtf32)
+{
+ return nUtf32 >= 0x30 && nUtf32 <= 0x39 ? // '0'--'9'
+ static_cast< int >(nUtf32 - 0x30) :
+ nUtf32 >= 0x41 && nUtf32 <= 0x46 ? // 'A'--'F'
+ static_cast< int >(nUtf32 - 0x41 + 10) :
+ nUtf32 >= 0x61 && nUtf32 <= 0x66 ? // 'a'--'f'
+ static_cast< int >(nUtf32 - 0x61 + 10) :
+ -1; // not a hex digit
+}
+
+bool isValid(sal_Bool const * pCharClass, sal_uInt32 nUtf32)
+{
+ return nUtf32 < rtl::UriCharClassSize && pCharClass[nUtf32];
+}
+
+void writeUnicode(rtl_uString ** pBuffer, sal_Int32 * pCapacity,
+ sal_Unicode cChar)
+{
+ rtl_uStringbuffer_insert(pBuffer, pCapacity, (*pBuffer)->length, &cChar, 1);
+}
+
+}
+
+namespace rtl::uri::detail {
+
+/** Read any of the following:
+
+ @li sequence of escape sequences representing character from eCharset,
+ translated to single UCS4 character; or
+ @li pair of UTF-16 surrogates, translated to single UCS4 character; or
+ @li single UTF-16 character, extended to UCS4 character.
+ */
+sal_uInt32 readUcs4(sal_Unicode const ** pBegin, sal_Unicode const * pEnd,
+ bool bEncoded, rtl_TextEncoding eCharset,
+ EscapeType * pType)
+{
+ sal_uInt32 nChar = *(*pBegin)++;
+ int nWeight1;
+ int nWeight2;
+ if (nChar == cEscapePrefix && bEncoded && pEnd - *pBegin >= 2
+ && (nWeight1 = getHexWeight((*pBegin)[0])) >= 0
+ && (nWeight2 = getHexWeight((*pBegin)[1])) >= 0)
+ {
+ *pBegin += 2;
+ nChar = static_cast< sal_uInt32 >(nWeight1 << 4 | nWeight2);
+ if (nChar <= 0x7F)
+ {
+ *pType = EscapeChar;
+ }
+ else if (eCharset == RTL_TEXTENCODING_UTF8)
+ {
+ if (nChar >= 0xC0 && nChar <= 0xF4)
+ {
+ sal_uInt32 nEncoded;
+ int nShift;
+ sal_uInt32 nMin;
+ if (nChar <= 0xDF)
+ {
+ nEncoded = (nChar & 0x1F) << 6;
+ nShift = 0;
+ nMin = 0x80;
+ }
+ else if (nChar <= 0xEF)
+ {
+ nEncoded = (nChar & 0x0F) << 12;
+ nShift = 6;
+ nMin = 0x800;
+ }
+ else
+ {
+ nEncoded = (nChar & 0x07) << 18;
+ nShift = 12;
+ nMin = 0x10000;
+ }
+
+ sal_Unicode const * p = *pBegin;
+ bool bUTF8 = true;
+
+ for (; nShift >= 0; nShift -= 6)
+ {
+ if (pEnd - p < 3 || p[0] != cEscapePrefix
+ || (nWeight1 = getHexWeight(p[1])) < 8
+ || nWeight1 > 11
+ || (nWeight2 = getHexWeight(p[2])) < 0)
+ {
+ bUTF8 = false;
+ break;
+ }
+ p += 3;
+ nEncoded |= ((nWeight1 & 3) << 4 | nWeight2) << nShift;
+ }
+ if (bUTF8 && rtl::isUnicodeScalarValue(nEncoded)
+ && nEncoded >= nMin)
+ {
+ *pBegin = p;
+ *pType = EscapeChar;
+ return nEncoded;
+ }
+ }
+ *pType = EscapeOctet;
+ }
+ else
+ {
+ OStringBuffer aBuf;
+ aBuf.append(static_cast< char >(nChar));
+ rtl_TextToUnicodeConverter aConverter
+ = rtl_createTextToUnicodeConverter(eCharset);
+ sal_Unicode const * p = *pBegin;
+
+ for (;;)
+ {
+ sal_Unicode aDst[2];
+ sal_uInt32 nInfo;
+ sal_Size nConverted;
+ sal_Size nDstSize = rtl_convertTextToUnicode(
+ aConverter, nullptr, aBuf.getStr(), aBuf.getLength(), aDst,
+ SAL_N_ELEMENTS( aDst ),
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR),
+ &nInfo, &nConverted);
+
+ if (nInfo == 0)
+ {
+ assert( nConverted
+ == sal::static_int_cast< sal_uInt32 >(
+ aBuf.getLength()));
+
+ rtl_destroyTextToUnicodeConverter(aConverter);
+ *pBegin = p;
+ *pType = EscapeChar;
+
+ assert( nDstSize == 1
+ || (nDstSize == 2 && rtl::isHighSurrogate(aDst[0])
+ && rtl::isLowSurrogate(aDst[1])));
+
+ return nDstSize == 1
+ ? aDst[0] : rtl::combineSurrogates(aDst[0], aDst[1]);
+ }
+ if (nInfo == RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOOSMALL
+ && pEnd - p >= 3 && p[0] == cEscapePrefix
+ && (nWeight1 = getHexWeight(p[1])) >= 0
+ && (nWeight2 = getHexWeight(p[2])) >= 0)
+ {
+ p += 3;
+ aBuf.append(static_cast< char >(nWeight1 << 4 | nWeight2));
+ }
+ else if (nInfo == RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOOSMALL
+ && p != pEnd && *p <= 0x7F)
+ {
+ aBuf.append(static_cast< char >(*p++));
+ }
+ else
+ {
+ assert(
+ (nInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL)
+ == 0);
+ break;
+ }
+ }
+ rtl_destroyTextToUnicodeConverter(aConverter);
+ *pType = EscapeOctet;
+ }
+ return nChar;
+ }
+
+ *pType = EscapeNo;
+ return rtl::isHighSurrogate(nChar) && *pBegin < pEnd
+ && rtl::isLowSurrogate(**pBegin) ?
+ rtl::combineSurrogates(nChar, *(*pBegin)++) : nChar;
+}
+
+}
+
+namespace {
+
+void writeUcs4(rtl_uString ** pBuffer, sal_Int32 * pCapacity, sal_uInt32 nUtf32)
+{
+ rtl_uStringbuffer_insertUtf32(pBuffer, pCapacity, (*pBuffer)->length, nUtf32);
+}
+
+void writeEscapeOctet(rtl_uString ** pBuffer, sal_Int32 * pCapacity,
+ sal_uInt32 nOctet)
+{
+ assert(nOctet <= 0xFF); // bad octet
+
+ static sal_Unicode const aHex[16]
+ = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 }; /* '0'--'9', 'A'--'F' */
+
+ writeUnicode(pBuffer, pCapacity, cEscapePrefix);
+ writeUnicode(pBuffer, pCapacity, aHex[nOctet >> 4]);
+ writeUnicode(pBuffer, pCapacity, aHex[nOctet & 15]);
+}
+
+bool writeEscapeChar(rtl_uString ** pBuffer, sal_Int32 * pCapacity,
+ sal_uInt32 nUtf32, rtl_TextEncoding eCharset, bool bStrict)
+{
+ assert(rtl::isUnicodeCodePoint(nUtf32));
+ if (eCharset == RTL_TEXTENCODING_UTF8)
+ {
+ if (nUtf32 < 0x80)
+ {
+ writeEscapeOctet(pBuffer, pCapacity, nUtf32);
+ }
+ else if (nUtf32 < 0x800)
+ {
+ writeEscapeOctet(pBuffer, pCapacity, nUtf32 >> 6 | 0xC0);
+ writeEscapeOctet(pBuffer, pCapacity, (nUtf32 & 0x3F) | 0x80);
+ }
+ else if (nUtf32 < 0x10000)
+ {
+ writeEscapeOctet(pBuffer, pCapacity, nUtf32 >> 12 | 0xE0);
+ writeEscapeOctet(pBuffer, pCapacity, (nUtf32 >> 6 & 0x3F) | 0x80);
+ writeEscapeOctet(pBuffer, pCapacity, (nUtf32 & 0x3F) | 0x80);
+ }
+ else
+ {
+ writeEscapeOctet(pBuffer, pCapacity, nUtf32 >> 18 | 0xF0);
+ writeEscapeOctet(pBuffer, pCapacity, (nUtf32 >> 12 & 0x3F) | 0x80);
+ writeEscapeOctet(pBuffer, pCapacity, (nUtf32 >> 6 & 0x3F) | 0x80);
+ writeEscapeOctet(pBuffer, pCapacity, (nUtf32 & 0x3F) | 0x80);
+ }
+ }
+ else
+ {
+ rtl_UnicodeToTextConverter aConverter
+ = rtl_createUnicodeToTextConverter(eCharset);
+ sal_Unicode aSrc[2];
+ sal_Size nSrcSize = rtl::splitSurrogates(nUtf32, aSrc);
+
+ char aDst[32]; // FIXME random value
+ sal_uInt32 nInfo;
+ sal_Size nConverted;
+ sal_Size nDstSize = rtl_convertUnicodeToText(
+ aConverter, nullptr, aSrc, nSrcSize, aDst, sizeof aDst,
+ RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
+ | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
+ | RTL_UNICODETOTEXT_FLAGS_FLUSH,
+ &nInfo, &nConverted);
+ assert((nInfo & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) == 0);
+ rtl_destroyUnicodeToTextConverter(aConverter);
+
+ if (nInfo == 0)
+ {
+ assert(nConverted == nSrcSize); // bad rtl_convertUnicodeToText
+
+ for (sal_Size i = 0; i < nDstSize; ++i)
+ {
+ writeEscapeOctet(pBuffer, pCapacity,
+ static_cast< unsigned char >(aDst[i]));
+ // FIXME all octets are escaped, even if there is no need
+ }
+ }
+ else
+ {
+ if (bStrict)
+ return false;
+
+ writeUcs4(pBuffer, pCapacity, nUtf32);
+ }
+ }
+ return true;
+}
+
+struct Component
+{
+ sal_Unicode const * pBegin;
+ sal_Unicode const * pEnd;
+
+ Component(): pBegin(nullptr), pEnd(nullptr) {}
+
+ bool isPresent() const { return pBegin != nullptr; }
+
+ sal_Int32 getLength() const;
+};
+
+sal_Int32 Component::getLength() const
+{
+ assert(isPresent()); // taking length of non-present component
+ return static_cast< sal_Int32 >(pEnd - pBegin);
+}
+
+struct Components
+{
+ Component aScheme;
+ Component aAuthority;
+ Component aPath;
+ Component aQuery;
+ Component aFragment;
+};
+
+void parseUriRef(rtl_uString const * pUriRef, Components * pComponents)
+{
+ // This algorithm is liberal and accepts various forms of illegal input.
+
+ sal_Unicode const * pBegin = pUriRef->buffer;
+ sal_Unicode const * pEnd = pBegin + pUriRef->length;
+ sal_Unicode const * pPos = pBegin;
+
+ if (pPos != pEnd && rtl::isAsciiAlpha(*pPos))
+ {
+ for (sal_Unicode const * p = pPos + 1; p != pEnd; ++p)
+ {
+ if (*p == ':')
+ {
+ pComponents->aScheme.pBegin = pBegin;
+ pComponents->aScheme.pEnd = ++p;
+ pPos = p;
+ break;
+ }
+
+ if (!rtl::isAsciiAlphanumeric(*p) && *p != '+' && *p != '-'
+ && *p != '.')
+ {
+ break;
+ }
+ }
+ }
+
+ if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/')
+ {
+ pComponents->aAuthority.pBegin = pPos;
+ pPos += 2;
+ while (pPos != pEnd && *pPos != '/' && *pPos != '?' && *pPos != '#')
+ {
+ ++pPos;
+ }
+
+ pComponents->aAuthority.pEnd = pPos;
+ }
+
+ pComponents->aPath.pBegin = pPos;
+ while (pPos != pEnd && *pPos != '?' && * pPos != '#')
+ {
+ ++pPos;
+ }
+
+ pComponents->aPath.pEnd = pPos;
+
+ if (pPos != pEnd && *pPos == '?')
+ {
+ pComponents->aQuery.pBegin = pPos++;
+ while (pPos != pEnd && * pPos != '#')
+ {
+ ++pPos;
+ }
+
+ pComponents->aQuery.pEnd = pPos;
+ }
+
+ if (pPos != pEnd)
+ {
+ assert(*pPos == '#');
+ pComponents->aFragment.pBegin = pPos;
+ pComponents->aFragment.pEnd = pEnd;
+ }
+}
+
+void appendPath(
+ OUStringBuffer & buffer, sal_Int32 bufferStart, bool precedingSlash,
+ sal_Unicode const * pathBegin, sal_Unicode const * pathEnd)
+{
+ while (precedingSlash || pathBegin != pathEnd)
+ {
+ sal_Unicode const * p = pathBegin;
+ while (p != pathEnd && *p != '/')
+ {
+ ++p;
+ }
+
+ std::size_t n = p - pathBegin;
+ if (n == 1 && pathBegin[0] == '.')
+ {
+ // input begins with "." -> remove from input (and done):
+ // i.e., !precedingSlash -> !precedingSlash
+ // input begins with "./" -> remove from input:
+ // i.e., !precedingSlash -> !precedingSlash
+ // input begins with "/." -> replace with "/" in input (and not yet
+ // done):
+ // i.e., precedingSlash -> precedingSlash
+ // input begins with "/./" -> replace with "/" in input:
+ // i.e., precedingSlash -> precedingSlash
+ }
+ else if (n == 2 && pathBegin[0] == '.' && pathBegin[1] == '.')
+ {
+ // input begins with ".." -> remove from input (and done):
+ // i.e., !precedingSlash -> !precedingSlash
+ // input begins with "../" -> remove from input
+ // i.e., !precedingSlash -> !precedingSlash
+ // input begins with "/.." -> replace with "/" in input, and shrink
+ // output (not yet done):
+ // i.e., precedingSlash -> precedingSlash
+ // input begins with "/../" -> replace with "/" in input, and shrink
+ // output:
+ // i.e., precedingSlash -> precedingSlash
+ if (precedingSlash)
+ {
+ buffer.truncate(
+ bufferStart
+ + std::max<sal_Int32>(
+ rtl_ustr_lastIndexOfChar_WithLength(
+ buffer.getStr() + bufferStart,
+ buffer.getLength() - bufferStart, '/'),
+ 0));
+ }
+ }
+ else
+ {
+ if (precedingSlash)
+ buffer.append('/');
+
+ buffer.append(pathBegin, n);
+ precedingSlash = p != pathEnd;
+ }
+ pathBegin = p + (p == pathEnd ? 0 : 1);
+ }
+}
+
+}
+
+sal_Bool const * SAL_CALL rtl_getUriCharClass(rtl_UriCharClass eCharClass)
+ SAL_THROW_EXTERN_C()
+{
+ static constexpr std::array<sal_Bool, rtl::UriCharClassSize> aCharClass[] = {
+ rtl::createUriCharClass(u8""), // None
+ rtl::createUriCharClass(
+ u8"!$&'()*+,-./:;=?@[]_~"
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // Uric
+ rtl::createUriCharClass(
+ u8"!$&'()*+,-.:;=?@_~"
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // UricNoSlash
+ rtl::createUriCharClass(
+ u8"!$&'()*+,-.;=@_~"
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // RelSegment
+ rtl::createUriCharClass(
+ u8"!$&'()*+,-.:;=@_~"
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // RegName
+ rtl::createUriCharClass(
+ u8"!$&'()*+,-.:;=_~"
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // Userinfo
+ rtl::createUriCharClass(
+ u8"!$&'()*+,-.:=@_~"
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // Pchar
+ rtl::createUriCharClass(
+ u8"!$&'()*+-./:?@_~"
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")}; // UnoParamValue
+
+ assert(
+ (eCharClass >= 0
+ && (sal::static_int_cast< std::size_t >(eCharClass)
+ < SAL_N_ELEMENTS(aCharClass)))); // bad eCharClass
+ return aCharClass[eCharClass].data();
+}
+
+void SAL_CALL rtl_uriEncode(rtl_uString * pText, sal_Bool const * pCharClass,
+ rtl_UriEncodeMechanism eMechanism,
+ rtl_TextEncoding eCharset, rtl_uString ** pResult)
+ SAL_THROW_EXTERN_C()
+{
+ assert(!pCharClass[0x25]); // make sure the percent sign is encoded...
+
+ sal_Unicode const * p = pText->buffer;
+ sal_Unicode const * pEnd = p + pText->length;
+ sal_Int32 nCapacity = 256;
+ rtl_uString_new_WithLength(pResult, nCapacity);
+
+ while (p < pEnd)
+ {
+ rtl::uri::detail::EscapeType eType;
+ sal_uInt32 nUtf32 = rtl::uri::detail::readUcs4(
+ &p, pEnd,
+ (eMechanism == rtl_UriEncodeKeepEscapes
+ || eMechanism == rtl_UriEncodeCheckEscapes
+ || eMechanism == rtl_UriEncodeStrictKeepEscapes),
+ eCharset, &eType);
+
+ switch (eType)
+ {
+ case rtl::uri::detail::EscapeNo:
+ if (isValid(pCharClass, nUtf32)) // implies nUtf32 <= 0x7F
+ {
+ writeUnicode(pResult, &nCapacity,
+ static_cast< sal_Unicode >(nUtf32));
+ }
+ else if (!writeEscapeChar(
+ pResult, &nCapacity, nUtf32, eCharset,
+ (eMechanism == rtl_UriEncodeStrict
+ || eMechanism == rtl_UriEncodeStrictKeepEscapes)))
+ {
+ rtl_uString_new(pResult);
+ return;
+ }
+ break;
+
+ case rtl::uri::detail::EscapeChar:
+ if (eMechanism == rtl_UriEncodeCheckEscapes
+ && isValid(pCharClass, nUtf32)) // implies nUtf32 <= 0x7F
+ {
+ writeUnicode(pResult, &nCapacity,
+ static_cast< sal_Unicode >(nUtf32));
+ }
+ else if (!writeEscapeChar(
+ pResult, &nCapacity, nUtf32, eCharset,
+ (eMechanism == rtl_UriEncodeStrict
+ || eMechanism == rtl_UriEncodeStrictKeepEscapes)))
+ {
+ rtl_uString_new(pResult);
+ return;
+ }
+ break;
+
+ case rtl::uri::detail::EscapeOctet:
+ writeEscapeOctet(pResult, &nCapacity, nUtf32);
+ break;
+ }
+ }
+ *pResult = rtl_uStringBuffer_makeStringAndClear(pResult, &nCapacity);
+}
+
+void SAL_CALL rtl_uriDecode(rtl_uString * pText,
+ rtl_UriDecodeMechanism eMechanism,
+ rtl_TextEncoding eCharset, rtl_uString ** pResult)
+ SAL_THROW_EXTERN_C()
+{
+ switch (eMechanism)
+ {
+ case rtl_UriDecodeNone:
+ rtl_uString_assign(pResult, pText);
+ break;
+
+ case rtl_UriDecodeToIuri:
+ eCharset = RTL_TEXTENCODING_UTF8;
+ [[fallthrough]];
+ default: // rtl_UriDecodeWithCharset, rtl_UriDecodeStrict
+ {
+ sal_Unicode const * p = pText->buffer;
+ sal_Unicode const * pEnd = p + pText->length;
+ sal_Int32 nCapacity = pText->length;
+ rtl_uString_new_WithLength(pResult, nCapacity);
+
+ while (p < pEnd)
+ {
+ rtl::uri::detail::EscapeType eType;
+ sal_uInt32 nUtf32 = rtl::uri::detail::readUcs4(&p, pEnd, true, eCharset, &eType);
+ switch (eType)
+ {
+ case rtl::uri::detail::EscapeChar:
+ if (nUtf32 <= 0x7F && eMechanism == rtl_UriDecodeToIuri)
+ {
+ writeEscapeOctet(pResult, &nCapacity, nUtf32);
+ break;
+ }
+ [[fallthrough]];
+
+ case rtl::uri::detail::EscapeNo:
+ writeUcs4(pResult, &nCapacity, nUtf32);
+ break;
+
+ case rtl::uri::detail::EscapeOctet:
+ if (eMechanism == rtl_UriDecodeStrict)
+ {
+ rtl_uString_new(pResult);
+ return;
+ }
+ writeEscapeOctet(pResult, &nCapacity, nUtf32);
+ break;
+ }
+ }
+
+ *pResult = rtl_uStringBuffer_makeStringAndClear( pResult, &nCapacity );
+ }
+ break;
+ }
+}
+
+sal_Bool SAL_CALL rtl_uriConvertRelToAbs(rtl_uString * pBaseUriRef,
+ rtl_uString * pRelUriRef,
+ rtl_uString ** pResult,
+ rtl_uString ** pException)
+ SAL_THROW_EXTERN_C()
+{
+ // Use the strict parser algorithm from RFC 3986, section 5.2, to turn the
+ // relative URI into an absolute one:
+ Components aRelComponents;
+ parseUriRef(pRelUriRef, &aRelComponents);
+ OUStringBuffer aBuffer(256);
+
+ if (aRelComponents.aScheme.isPresent())
+ {
+ aBuffer.append(aRelComponents.aScheme.pBegin,
+ aRelComponents.aScheme.getLength());
+
+ if (aRelComponents.aAuthority.isPresent())
+ {
+ aBuffer.append(aRelComponents.aAuthority.pBegin,
+ aRelComponents.aAuthority.getLength());
+ }
+
+ appendPath(
+ aBuffer, aBuffer.getLength(), false, aRelComponents.aPath.pBegin,
+ aRelComponents.aPath.pEnd);
+
+ if (aRelComponents.aQuery.isPresent())
+ {
+ aBuffer.append(aRelComponents.aQuery.pBegin,
+ aRelComponents.aQuery.getLength());
+ }
+ }
+ else
+ {
+ Components aBaseComponents;
+ parseUriRef(pBaseUriRef, &aBaseComponents);
+ if (!aBaseComponents.aScheme.isPresent())
+ {
+ rtl_uString_assign(
+ pException,
+ (OUString(
+ "<" + OUString::unacquired(&pBaseUriRef)
+ + "> does not start with a scheme component")
+ .pData));
+ return false;
+ }
+
+ aBuffer.append(aBaseComponents.aScheme.pBegin,
+ aBaseComponents.aScheme.getLength());
+ if (aRelComponents.aAuthority.isPresent())
+ {
+ aBuffer.append(aRelComponents.aAuthority.pBegin,
+ aRelComponents.aAuthority.getLength());
+ appendPath(
+ aBuffer, aBuffer.getLength(), false,
+ aRelComponents.aPath.pBegin, aRelComponents.aPath.pEnd);
+
+ if (aRelComponents.aQuery.isPresent())
+ {
+ aBuffer.append(aRelComponents.aQuery.pBegin,
+ aRelComponents.aQuery.getLength());
+ }
+ }
+ else
+ {
+ if (aBaseComponents.aAuthority.isPresent())
+ {
+ aBuffer.append(aBaseComponents.aAuthority.pBegin,
+ aBaseComponents.aAuthority.getLength());
+ }
+
+ if (aRelComponents.aPath.pBegin == aRelComponents.aPath.pEnd)
+ {
+ aBuffer.append(aBaseComponents.aPath.pBegin,
+ aBaseComponents.aPath.getLength());
+ if (aRelComponents.aQuery.isPresent())
+ {
+ aBuffer.append(aRelComponents.aQuery.pBegin,
+ aRelComponents.aQuery.getLength());
+ }
+ else if (aBaseComponents.aQuery.isPresent())
+ {
+ aBuffer.append(aBaseComponents.aQuery.pBegin,
+ aBaseComponents.aQuery.getLength());
+ }
+ }
+ else
+ {
+ if (*aRelComponents.aPath.pBegin == '/')
+ {
+ appendPath(
+ aBuffer, aBuffer.getLength(), false,
+ aRelComponents.aPath.pBegin, aRelComponents.aPath.pEnd);
+ }
+ else if (aBaseComponents.aAuthority.isPresent()
+ && aBaseComponents.aPath.pBegin
+ == aBaseComponents.aPath.pEnd)
+ {
+ appendPath(
+ aBuffer, aBuffer.getLength(), true,
+ aRelComponents.aPath.pBegin, aRelComponents.aPath.pEnd);
+ }
+ else
+ {
+ sal_Int32 n = aBuffer.getLength();
+ sal_Int32 i = rtl_ustr_lastIndexOfChar_WithLength(
+ aBaseComponents.aPath.pBegin,
+ aBaseComponents.aPath.getLength(), '/');
+
+ if (i >= 0)
+ {
+ appendPath(
+ aBuffer, n, false, aBaseComponents.aPath.pBegin,
+ aBaseComponents.aPath.pBegin + i);
+ }
+
+ appendPath(
+ aBuffer, n, i >= 0, aRelComponents.aPath.pBegin,
+ aRelComponents.aPath.pEnd);
+ }
+
+ if (aRelComponents.aQuery.isPresent())
+ {
+ aBuffer.append(aRelComponents.aQuery.pBegin,
+ aRelComponents.aQuery.getLength());
+ }
+ }
+ }
+ }
+ if (aRelComponents.aFragment.isPresent())
+ {
+ aBuffer.append(aRelComponents.aFragment.pBegin,
+ aRelComponents.aFragment.getLength());
+ }
+
+ rtl_uString_assign(pResult, aBuffer.makeStringAndClear().pData);
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/ustrbuf.cxx b/sal/rtl/ustrbuf.cxx
new file mode 100644
index 000000000..c91834cfa
--- /dev/null
+++ b/sal/rtl/ustrbuf.cxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+
+#include <osl/interlck.h>
+#include <osl/diagnose.h>
+#include <rtl/character.hxx>
+#include <rtl/ustrbuf.hxx>
+#include "strimp.hxx"
+
+#if USE_SDT_PROBES
+#define RTL_LOG_STRING_BITS 16
+#endif
+
+#include "strtmpl.hxx"
+
+void SAL_CALL rtl_uStringbuffer_newFromStr_WithLength( rtl_uString ** newStr,
+ const sal_Unicode * value,
+ sal_Int32 count)
+{
+ rtl::str::stringbuffer_newFromStr_WithLength(newStr, value, count);
+}
+
+rtl_uString * SAL_CALL rtl_uStringBuffer_refReturn( rtl_uString * pThis )
+{
+ RTL_LOG_STRING_NEW( pThis );
+ rtl::str::acquire( pThis );
+ return pThis;
+}
+
+rtl_uString * SAL_CALL rtl_uStringBuffer_makeStringAndClear( rtl_uString ** ppThis,
+ sal_Int32 *nCapacity )
+{
+ assert(ppThis);
+ assert(nCapacity);
+ // avoid an un-necessary atomic ref/unref pair
+ rtl_uString *pStr = *ppThis;
+ *ppThis = nullptr;
+
+ rtl::str::new_(ppThis);
+ *nCapacity = 0;
+
+ RTL_LOG_STRING_NEW( pStr );
+
+ return pStr;
+}
+
+sal_Int32 SAL_CALL rtl_uStringbuffer_newFromStringBuffer( rtl_uString ** newStr,
+ sal_Int32 capacity,
+ rtl_uString * oldStr )
+{
+ return rtl::str::stringbuffer_newFromStringBuffer(newStr, capacity, oldStr);
+}
+
+void SAL_CALL rtl_uStringbuffer_ensureCapacity
+ (rtl_uString ** This, sal_Int32* capacity, sal_Int32 minimumCapacity)
+{
+ rtl::str::stringbuffer_ensureCapacity(This, capacity, minimumCapacity);
+}
+
+void SAL_CALL rtl_uStringbuffer_insert( rtl_uString ** This,
+ sal_Int32 * capacity,
+ sal_Int32 offset,
+ const sal_Unicode * str,
+ sal_Int32 len)
+{
+ rtl::str::stringbuffer_insert(This, capacity, offset, str, len);
+}
+
+void rtl_uStringbuffer_insertUtf32(
+ rtl_uString ** pThis, sal_Int32 * capacity, sal_Int32 offset, sal_uInt32 c)
+ SAL_THROW_EXTERN_C()
+{
+ sal_Unicode buf[2];
+ sal_Int32 len = rtl::splitSurrogates(c, buf);
+ rtl::str::stringbuffer_insert(pThis, capacity, offset, buf, len);
+}
+
+void SAL_CALL rtl_uStringbuffer_insert_ascii( /*inout*/rtl_uString ** This,
+ /*inout*/sal_Int32 * capacity,
+ sal_Int32 offset,
+ const char * str,
+ sal_Int32 len)
+{
+ rtl::str::stringbuffer_insert(This, capacity, offset, str, len);
+}
+
+/*************************************************************************
+ * rtl_uStringbuffer_remove
+ */
+void SAL_CALL rtl_uStringbuffer_remove( rtl_uString ** This,
+ sal_Int32 start,
+ sal_Int32 len )
+{
+ rtl::str::stringbuffer_remove(This, start, len);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/ustring.cxx b/sal/rtl/ustring.cxx
new file mode 100644
index 000000000..6fc0e8707
--- /dev/null
+++ b/sal/rtl/ustring.cxx
@@ -0,0 +1,1324 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <cstdlib>
+#include <limits>
+#include <stdexcept>
+#include <string>
+
+#include <config_options.h>
+#include <osl/diagnose.h>
+#include <osl/interlck.h>
+#include <osl/mutex.h>
+#include <rtl/tencinfo.h>
+
+#include <string.h>
+#include <sal/alloca.h>
+#include <sal/log.hxx>
+
+#include "hash.hxx"
+#include "strimp.hxx"
+#include <rtl/character.hxx>
+#include <rtl/ustring.h>
+
+#include <rtl/math.h>
+
+#if defined _WIN32
+// Temporary check to verify that the #pragma pack around rtl_uString is indeed cargo cult and can
+// safely be removed:
+static_assert(alignof (rtl_uString) == 4);
+static_assert(sizeof (rtl_uString) == 12);
+#endif
+
+/* ======================================================================= */
+
+#if USE_SDT_PROBES
+#define RTL_LOG_STRING_BITS 16
+#endif
+
+#include "strtmpl.hxx"
+
+/* ======================================================================= */
+
+sal_Int32 rtl_ustr_indexOfAscii_WithLength(
+ sal_Unicode const * str, sal_Int32 len,
+ char const * subStr, sal_Int32 subLen) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::indexOfStr_WithLength(str, len, subStr, subLen);
+}
+
+sal_Int32 rtl_ustr_lastIndexOfAscii_WithLength(
+ sal_Unicode const * str, sal_Int32 len,
+ char const * subStr, sal_Int32 subLen) SAL_THROW_EXTERN_C()
+{
+ assert(len >= 0);
+ assert(subLen >= 0);
+ if (subLen > 0 && subLen <= len) {
+ sal_Int32 i;
+ for (i = len - subLen; i >= 0; --i) {
+ if (rtl_ustr_asciil_reverseEquals_WithLength(
+ str + i, subStr, subLen))
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+sal_Int32 SAL_CALL rtl_ustr_valueOfFloat(sal_Unicode * pStr, float f)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfFP<RTL_USTR_MAX_VALUEOFFLOAT>(pStr, f);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_valueOfDouble(sal_Unicode * pStr, double d)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfFP<RTL_USTR_MAX_VALUEOFDOUBLE>(pStr, d);
+}
+
+namespace {
+
+// Avoid -fsanitize=undefined warning e.g. "runtime error: value 1e+99 is
+// outside the range of representable values of type 'float'" with Clang prior to
+// <https://github.com/llvm/llvm-project/commit/9e52c43090f8cd980167bbd2719878ae36bcf6b5> "Treat the
+// range of representable values of floating-point types as [-inf, +inf] not as [-max, +max]"
+// (ENABLE_RUNTIME_OPTIMIZATIONS is an approximation for checking whether building is done without
+// -fsanitize=undefined):
+float doubleToFloat(double x) {
+#if !defined __clang__ || __clang_major__ >= 9 || ENABLE_RUNTIME_OPTIMIZATIONS
+ return static_cast<float>(x);
+#else
+ return
+ x < -std::numeric_limits<float>::max()
+ ? -std::numeric_limits<float>::infinity()
+ : x > std::numeric_limits<float>::max()
+ ? std::numeric_limits<float>::infinity()
+ : static_cast<float>(x);
+#endif
+}
+
+}
+
+float SAL_CALL rtl_ustr_toFloat(sal_Unicode const * pStr) SAL_THROW_EXTERN_C()
+{
+ assert(pStr);
+ return doubleToFloat(rtl_math_uStringToDouble(pStr,
+ pStr + rtl_ustr_getLength(pStr),
+ '.', 0, nullptr, nullptr));
+}
+
+double SAL_CALL rtl_ustr_toDouble(sal_Unicode const * pStr) SAL_THROW_EXTERN_C()
+{
+ assert(pStr);
+ return rtl_math_uStringToDouble(pStr, pStr + rtl_ustr_getLength(pStr), '.',
+ 0, nullptr, nullptr);
+}
+
+/* ======================================================================= */
+
+sal_Int32 SAL_CALL rtl_ustr_ascii_compare( const sal_Unicode* pStr1,
+ const char* pStr2 )
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::null_terminated(pStr1), rtl::str::null_terminated(pStr2),
+ rtl::str::CompareNormal(), rtl::str::noShortening);
+}
+
+/* ----------------------------------------------------------------------- */
+
+sal_Int32 SAL_CALL rtl_ustr_ascii_compare_WithLength( const sal_Unicode* pStr1,
+ sal_Int32 nStr1Len,
+ const char* pStr2 )
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len),
+ rtl::str::null_terminated(pStr2),
+ rtl::str::CompareNormal(), rtl::str::noShortening);
+}
+
+/* ----------------------------------------------------------------------- */
+
+sal_Int32 SAL_CALL rtl_ustr_ascii_shortenedCompare_WithLength( const sal_Unicode* pStr1,
+ sal_Int32 nStr1Len,
+ const char* pStr2,
+ sal_Int32 nShortenedLength )
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len),
+ rtl::str::null_terminated(pStr2),
+ rtl::str::CompareNormal(), nShortenedLength);
+}
+
+/* ----------------------------------------------------------------------- */
+
+sal_Int32 SAL_CALL rtl_ustr_asciil_reverseCompare_WithLength( const sal_Unicode* pStr1,
+ sal_Int32 nStr1Len,
+ const char* pStr2,
+ sal_Int32 nStr2Len )
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::reverseCompare_WithLengths(pStr1, nStr1Len, pStr2, nStr2Len,
+ rtl::str::CompareNormal());
+}
+
+/* ----------------------------------------------------------------------- */
+
+sal_Bool SAL_CALL rtl_ustr_asciil_reverseEquals_WithLength( const sal_Unicode* pStr1,
+ const char* pStr2,
+ sal_Int32 nStrLen )
+ SAL_THROW_EXTERN_C()
+{
+ assert(nStrLen >= 0);
+ const sal_Unicode* pStr1Run = pStr1+nStrLen;
+ const char* pStr2Run = pStr2+nStrLen;
+ while ( pStr1 < pStr1Run )
+ {
+ SAL_WARN_IF( !rtl::isAscii(static_cast<unsigned char>(*pStr2)), "rtl.string",
+ "rtl_ustr_asciil_reverseEquals_WithLength - Found char > 127" );
+ pStr1Run--;
+ pStr2Run--;
+ if( *pStr1Run != static_cast<sal_Unicode>(*pStr2Run) )
+ return false;
+ }
+
+ return true;
+}
+
+/* ----------------------------------------------------------------------- */
+
+sal_Int32 SAL_CALL rtl_ustr_ascii_compareIgnoreAsciiCase( const sal_Unicode* pStr1,
+ const char* pStr2 )
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::null_terminated(pStr1), rtl::str::null_terminated(pStr2),
+ rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening);
+}
+
+/* ----------------------------------------------------------------------- */
+
+sal_Int32 SAL_CALL rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( const sal_Unicode* pStr1,
+ sal_Int32 nStr1Len,
+ const char* pStr2 )
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len),
+ rtl::str::null_terminated(pStr2),
+ rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening);
+}
+
+sal_Int32 rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
+ sal_Unicode const * first, sal_Int32 firstLen,
+ char const * second, sal_Int32 secondLen) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(first, firstLen),
+ rtl::str::with_length(second, secondLen),
+ rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening);
+}
+
+/* ----------------------------------------------------------------------- */
+
+sal_Int32 SAL_CALL rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( const sal_Unicode* pStr1,
+ sal_Int32 nStr1Len,
+ const char* pStr2,
+ sal_Int32 nShortenedLength )
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len),
+ rtl::str::null_terminated(pStr2),
+ rtl::str::CompareIgnoreAsciiCase(), nShortenedLength);
+}
+
+/* ----------------------------------------------------------------------- */
+
+void SAL_CALL rtl_uString_newFromAscii( rtl_uString** ppThis,
+ const char* pCharStr )
+ SAL_THROW_EXTERN_C()
+{
+ assert(ppThis);
+ sal_Int32 nLen = pCharStr ? rtl::str::getLength(pCharStr) : 0;
+
+ if ( !nLen )
+ {
+ rtl_uString_new( ppThis );
+ return;
+ }
+
+ if ( *ppThis )
+ rtl_uString_release( *ppThis );
+
+ *ppThis = rtl_uString_ImplAlloc( nLen );
+ OSL_ASSERT(*ppThis != nullptr);
+ if ( !(*ppThis) )
+ return;
+
+ sal_Unicode* pBuffer = (*ppThis)->buffer;
+ do
+ {
+ assert(static_cast<unsigned char>(*pCharStr) < 0x80); // ASCII range
+ *pBuffer = *pCharStr;
+ pBuffer++;
+ pCharStr++;
+ }
+ while ( *pCharStr );
+
+ RTL_LOG_STRING_NEW( *ppThis );
+}
+
+void SAL_CALL rtl_uString_newFromCodePoints(
+ rtl_uString ** newString, sal_uInt32 const * codePoints,
+ sal_Int32 codePointCount) SAL_THROW_EXTERN_C()
+{
+ sal_Int32 n;
+ sal_Int32 i;
+ sal_Unicode * p;
+ assert(newString != nullptr);
+ assert((codePoints != nullptr || codePointCount == 0) && codePointCount >= 0);
+ if (codePointCount == 0) {
+ rtl_uString_new(newString);
+ return;
+ }
+ if (*newString != nullptr) {
+ rtl_uString_release(*newString);
+ }
+ n = codePointCount;
+ for (i = 0; i < codePointCount; ++i) {
+ OSL_ASSERT(rtl::isUnicodeCodePoint(codePoints[i]));
+ if (codePoints[i] >= 0x10000) {
+ ++n;
+ }
+ }
+ /* Builds on the assumption that sal_Int32 uses 32 bit two's complement
+ representation with wrap around (the necessary number of UTF-16 code
+ units will be no larger than 2 * SAL_MAX_INT32, represented as
+ sal_Int32 -2): */
+ if (n < 0) {
+ // coverity[dead_error_begin] - assumes wrap around
+ *newString = nullptr;
+ return;
+ }
+ *newString = rtl_uString_ImplAlloc(n);
+ if (*newString == nullptr) {
+ return;
+ }
+ p = (*newString)->buffer;
+ for (i = 0; i < codePointCount; ++i) {
+ p += rtl::splitSurrogates(codePoints[i], p);
+ }
+ RTL_LOG_STRING_NEW( *newString );
+}
+
+void rtl_uString_newConcatAsciiL(
+ rtl_uString ** newString, rtl_uString * left, char const * right,
+ sal_Int32 rightLength)
+{
+ rtl::str::newConcat(newString, left, right, rightLength);
+}
+
+void rtl_uString_newConcatUtf16L(
+ rtl_uString ** newString, rtl_uString * left, sal_Unicode const * right,
+ sal_Int32 rightLength)
+{
+ rtl::str::newConcat(newString, left, right, rightLength);
+}
+
+/* ======================================================================= */
+
+static int rtl_ImplGetFastUTF8UnicodeLen( const char* pStr, sal_Int32 nLen, bool * ascii )
+{
+ int n;
+ const char* pEndStr;
+
+ *ascii = true;
+ n = 0;
+ pEndStr = pStr+nLen;
+ while ( pStr < pEndStr )
+ {
+ unsigned char c = static_cast<unsigned char>(*pStr);
+
+ if ( !(c & 0x80) )
+ pStr++;
+ else
+ {
+ if ( (c & 0xE0) == 0xC0 )
+ pStr += 2;
+ else if ( (c & 0xF0) == 0xE0 )
+ pStr += 3;
+ else if ( (c & 0xF8) == 0xF0 )
+ pStr += 4;
+ else if ( (c & 0xFC) == 0xF8 )
+ pStr += 5;
+ else if ( (c & 0xFE) == 0xFC )
+ pStr += 6;
+ else
+ pStr++;
+ *ascii = false;
+ }
+
+ n++;
+ }
+
+ return n;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void rtl_string2UString_status( rtl_uString** ppThis,
+ const char* pStr,
+ sal_Int32 nLen,
+ rtl_TextEncoding eTextEncoding,
+ sal_uInt32 nCvtFlags,
+ sal_uInt32 *pInfo )
+{
+ OSL_ENSURE(nLen == 0 || rtl_isOctetTextEncoding(eTextEncoding),
+ "rtl_string2UString_status() - Wrong TextEncoding" );
+
+ if ( !nLen )
+ {
+ rtl_uString_new( ppThis );
+ if (pInfo != nullptr) {
+ *pInfo = 0;
+ }
+ }
+ else
+ {
+ if ( *ppThis )
+ rtl_uString_release( *ppThis );
+
+ /* Optimization for US-ASCII */
+ if ( eTextEncoding == RTL_TEXTENCODING_ASCII_US )
+ {
+ sal_Unicode* pBuffer;
+ *ppThis = rtl_uString_ImplAlloc( nLen );
+ if (*ppThis == nullptr) {
+ if (pInfo != nullptr) {
+ *pInfo = RTL_TEXTTOUNICODE_INFO_ERROR |
+ RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL;
+ }
+ return;
+ }
+ pBuffer = (*ppThis)->buffer;
+ sal_Int32 nLenCopy(nLen);
+ const char *pStrCopy(pStr);
+ do
+ {
+ /* Check ASCII range */
+ if (static_cast<unsigned char>(*pStrCopy) > 127)
+ {
+ rtl_uString_release(*ppThis);
+ goto retry; // cancel loop - try again with the converter
+ }
+
+ *pBuffer = *pStrCopy;
+ pBuffer++;
+ pStrCopy++;
+ nLenCopy--;
+ }
+ while (nLenCopy);
+ if (pInfo != nullptr) {
+ *pInfo = 0;
+ }
+ RTL_LOG_STRING_NEW( *ppThis );
+ return;
+ }
+retry:
+ {
+ rtl_uString* pTemp;
+ rtl_uString* pTemp2 = nullptr;
+ rtl_TextToUnicodeConverter hConverter;
+ sal_uInt32 nInfo;
+ sal_Size nSrcBytes;
+ sal_Size nDestChars;
+ sal_Size nNewLen;
+
+ /* Optimization for UTF-8 - we try to calculate the exact length */
+ /* For all other encoding we try the maximum - and reallocate
+ the buffer if needed */
+ if ( eTextEncoding == RTL_TEXTENCODING_UTF8 )
+ {
+ bool ascii;
+ nNewLen = rtl_ImplGetFastUTF8UnicodeLen( pStr, nLen, &ascii );
+ /* Includes the string only ASCII, then we could copy
+ the buffer faster */
+ if ( ascii )
+ {
+ sal_Unicode* pBuffer;
+ *ppThis = rtl_uString_ImplAlloc( nLen );
+ if (*ppThis == nullptr)
+ {
+ if (pInfo != nullptr) {
+ *pInfo = RTL_TEXTTOUNICODE_INFO_ERROR |
+ RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL;
+ }
+ return;
+ }
+ pBuffer = (*ppThis)->buffer;
+ do
+ {
+ assert((static_cast<unsigned char>(*pStr)) <= 127);
+ *pBuffer = *pStr;
+ pBuffer++;
+ pStr++;
+ nLen--;
+ }
+ while ( nLen );
+ if (pInfo != nullptr) {
+ *pInfo = 0;
+ }
+ RTL_LOG_STRING_NEW( *ppThis );
+ return;
+ }
+ }
+ else
+ nNewLen = nLen;
+
+ nCvtFlags |= RTL_TEXTTOUNICODE_FLAGS_FLUSH;
+ hConverter = rtl_createTextToUnicodeConverter( eTextEncoding );
+
+ pTemp = rtl_uString_ImplAlloc( nNewLen );
+ if (pTemp == nullptr) {
+ if (pInfo != nullptr) {
+ *pInfo = RTL_TEXTTOUNICODE_INFO_ERROR |
+ RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL;
+ }
+ return;
+ }
+ nDestChars = rtl_convertTextToUnicode( hConverter, nullptr,
+ pStr, nLen,
+ pTemp->buffer, nNewLen,
+ nCvtFlags,
+ &nInfo, &nSrcBytes );
+
+ /* Buffer not big enough, try again with enough space */
+ /* Shouldn't be the case, but if we get textencoding which
+ could results in more unicode characters we have this
+ code here. Could be the case for apple encodings */
+ while ( nInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL )
+ {
+ rtl_freeString( pTemp );
+ nNewLen += 8;
+ pTemp = rtl_uString_ImplAlloc( nNewLen );
+ if (pTemp == nullptr) {
+ if (pInfo != nullptr) {
+ *pInfo = RTL_TEXTTOUNICODE_INFO_ERROR |
+ RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL;
+ }
+ return;
+ }
+ nDestChars = rtl_convertTextToUnicode( hConverter, nullptr,
+ pStr, nLen,
+ pTemp->buffer, nNewLen,
+ nCvtFlags,
+ &nInfo, &nSrcBytes );
+ }
+
+ if (pInfo)
+ *pInfo = nInfo;
+
+ /* Set the buffer to the correct size or if there is too
+ much overhead, reallocate to the correct size */
+ if ( nNewLen > nDestChars+8 )
+ {
+ pTemp2 = rtl_uString_ImplAlloc( nDestChars );
+ }
+ if (pTemp2 != nullptr)
+ {
+ rtl::str::Copy(pTemp2->buffer, pTemp->buffer, nDestChars);
+ rtl_freeString(pTemp);
+ pTemp = pTemp2;
+ }
+ else
+ {
+ pTemp->length = nDestChars;
+ pTemp->buffer[nDestChars] = 0;
+ }
+
+ rtl_destroyTextToUnicodeConverter( hConverter );
+ *ppThis = pTemp;
+
+ /* Results the conversion in an empty buffer -
+ create an empty string */
+ if ( pTemp && !nDestChars )
+ rtl_uString_new( ppThis );
+ }
+ }
+ RTL_LOG_STRING_NEW( *ppThis );
+}
+
+void SAL_CALL rtl_string2UString( rtl_uString** ppThis,
+ const char* pStr,
+ sal_Int32 nLen,
+ rtl_TextEncoding eTextEncoding,
+ sal_uInt32 nCvtFlags ) SAL_THROW_EXTERN_C()
+{
+ assert(ppThis);
+ assert(nLen >= 0);
+ rtl_string2UString_status( ppThis, pStr, nLen, eTextEncoding,
+ nCvtFlags, nullptr );
+}
+
+/* ----------------------------------------------------------------------- */
+
+namespace {
+
+enum StrLifecycle {
+ CANNOT_RETURN,
+ CAN_RETURN = 1
+};
+
+}
+
+static oslMutex
+getInternMutex()
+{
+ static oslMutex pPoolGuard = osl_createMutex();
+
+ return pPoolGuard;
+}
+
+/* returns true if we found a dup in the pool */
+static void rtl_ustring_intern_internal( rtl_uString ** newStr,
+ rtl_uString * str,
+ StrLifecycle can_return )
+{
+ oslMutex pPoolMutex;
+
+ pPoolMutex = getInternMutex();
+
+ osl_acquireMutex( pPoolMutex );
+
+ *newStr = rtl_str_hash_intern (str, can_return);
+
+ osl_releaseMutex( pPoolMutex );
+
+ RTL_LOG_STRING_INTERN_NEW(*newStr, str);
+
+ if( can_return && *newStr != str )
+ { /* we dupped, then found a match */
+ rtl_freeString( str );
+ }
+}
+
+void SAL_CALL rtl_uString_intern( rtl_uString ** newStr,
+ rtl_uString * str) SAL_THROW_EXTERN_C()
+{
+ assert(newStr);
+ assert(str);
+ if (SAL_STRING_IS_INTERN(str))
+ {
+ rtl::str::acquire(str);
+ *newStr = str;
+ }
+ else
+ {
+ rtl_uString *pOrg = *newStr;
+ *newStr = nullptr;
+ rtl_ustring_intern_internal( newStr, str, CANNOT_RETURN );
+ if (pOrg)
+ rtl_uString_release (pOrg);
+ }
+}
+
+static int rtl_canGuessUOutputLength( int len, rtl_TextEncoding eTextEncoding )
+{
+ // FIXME: Maybe we should use a bit flag in the higher bits of the
+ // eTextEncoding value itself to determine the encoding type. But if we
+ // do, be sure to mask the value in certain places that expect the values
+ // to be numbered serially from 0 and up. One such place is
+ // Impl_getTextEncodingData().
+
+ switch ( eTextEncoding )
+ {
+ // 1 to 1 (with no zero elements)
+ case RTL_TEXTENCODING_IBM_437:
+ case RTL_TEXTENCODING_IBM_850:
+ case RTL_TEXTENCODING_IBM_860:
+ case RTL_TEXTENCODING_IBM_861:
+ case RTL_TEXTENCODING_IBM_863:
+ case RTL_TEXTENCODING_IBM_865:
+ return len;
+ }
+ return 0;
+}
+
+void SAL_CALL rtl_uString_internConvert( rtl_uString ** newStr,
+ const char * str,
+ sal_Int32 len,
+ rtl_TextEncoding eTextEncoding,
+ sal_uInt32 convertFlags,
+ sal_uInt32 * pInfo )
+ SAL_THROW_EXTERN_C()
+{
+ assert(newStr);
+ assert(len >= 0);
+ rtl_uString *scratch;
+
+ if (*newStr)
+ {
+ rtl_uString_release (*newStr);
+ *newStr = nullptr;
+ }
+
+ if ( len < 256 )
+ { // try various optimisations
+ sal_Int32 ulen;
+ if ( eTextEncoding == RTL_TEXTENCODING_ASCII_US )
+ {
+ int i;
+ rtl_uString *pScratch;
+ pScratch = static_cast< rtl_uString * >(
+ alloca(sizeof (rtl_uString) + len * sizeof (sal_Unicode)));
+ for (i = 0; i < len; i++)
+ {
+ /* Check ASCII range */
+ SAL_WARN_IF( !rtl::isAscii(static_cast<unsigned char>(str[i])), "rtl.string",
+ "rtl_ustring_internConvert() - Found char > 127 and RTL_TEXTENCODING_ASCII_US is specified" );
+ pScratch->buffer[i] = str[i];
+ }
+ pScratch->length = len;
+ rtl_ustring_intern_internal( newStr, pScratch, CANNOT_RETURN );
+ return;
+ }
+ if ( (ulen = rtl_canGuessUOutputLength(len, eTextEncoding)) != 0 )
+ {
+ rtl_uString *pScratch;
+ rtl_TextToUnicodeConverter hConverter;
+ sal_Size nSrcBytes;
+ sal_uInt32 nInfo;
+
+ pScratch = static_cast< rtl_uString * >(
+ alloca(
+ sizeof (rtl_uString) + ulen * sizeof (sal_Unicode)));
+
+ hConverter = rtl_createTextToUnicodeConverter( eTextEncoding );
+ rtl_convertTextToUnicode(
+ hConverter, nullptr, str, len, pScratch->buffer, ulen, convertFlags, &nInfo, &nSrcBytes );
+ rtl_destroyTextToUnicodeConverter( hConverter );
+
+ if (pInfo)
+ *pInfo = nInfo;
+
+ pScratch->length = ulen;
+ rtl_ustring_intern_internal( newStr, pScratch, CANNOT_RETURN );
+ return;
+ }
+
+ /* FIXME: we want a nice UTF-8 / alloca shortcut here */
+ }
+
+ scratch = nullptr;
+ rtl_string2UString_status( &scratch, str, len, eTextEncoding, convertFlags,
+ pInfo );
+ if (!scratch) {
+ return;
+ }
+ rtl_ustring_intern_internal( newStr, scratch, CAN_RETURN );
+}
+
+void internRelease (rtl_uString *pThis)
+{
+ rtl_uString *pFree = nullptr;
+ if ( SAL_STRING_REFCOUNT(
+ osl_atomic_decrement( &(pThis->refCount) ) ) == 0)
+ {
+ RTL_LOG_STRING_INTERN_DELETE(pThis);
+ oslMutex pPoolMutex = getInternMutex();
+ osl_acquireMutex( pPoolMutex );
+
+ rtl_str_hash_remove (pThis);
+
+ /* May have been separately acquired */
+ if ( SAL_STRING_REFCOUNT(
+ osl_atomic_increment( &(pThis->refCount) ) ) == 1 )
+ {
+ /* we got the last ref */
+ pFree = pThis;
+ }
+ else /* very unusual */
+ {
+ internRelease (pThis);
+ }
+
+ osl_releaseMutex( pPoolMutex );
+ }
+ if (pFree)
+ rtl_freeString (pFree);
+}
+
+sal_uInt32 SAL_CALL rtl_uString_iterateCodePoints(
+ rtl_uString const * string, sal_Int32 * indexUtf16,
+ sal_Int32 incrementCodePoints)
+{
+ sal_Int32 n;
+ sal_Unicode cu;
+ sal_uInt32 cp;
+ assert(string != nullptr && indexUtf16 != nullptr);
+ n = *indexUtf16;
+ assert(n >= 0 && n <= string->length);
+ while (incrementCodePoints < 0) {
+ assert(n > 0);
+ cu = string->buffer[--n];
+ if (rtl::isLowSurrogate(cu) && n != 0 &&
+ rtl::isHighSurrogate(string->buffer[n - 1]))
+ {
+ --n;
+ }
+ ++incrementCodePoints;
+ }
+ assert(n >= 0 && n < string->length);
+ cu = string->buffer[n];
+ if (rtl::isHighSurrogate(cu) && string->length - n >= 2 &&
+ rtl::isLowSurrogate(string->buffer[n + 1]))
+ {
+ cp = rtl::combineSurrogates(cu, string->buffer[n + 1]);
+ } else {
+ cp = cu;
+ }
+ while (incrementCodePoints > 0) {
+ assert(n < string->length);
+ cu = string->buffer[n++];
+ if (rtl::isHighSurrogate(cu) && n != string->length &&
+ rtl::isLowSurrogate(string->buffer[n]))
+ {
+ ++n;
+ }
+ --incrementCodePoints;
+ }
+ assert(n >= 0 && n <= string->length);
+ *indexUtf16 = n;
+ return cp;
+}
+
+sal_Bool rtl_convertStringToUString(
+ rtl_uString ** target, char const * source, sal_Int32 length,
+ rtl_TextEncoding encoding, sal_uInt32 flags) SAL_THROW_EXTERN_C()
+{
+ assert(target);
+ assert(length >= 0);
+ sal_uInt32 info;
+ rtl_string2UString_status(target, source, length, encoding, flags, &info);
+ return (info & RTL_TEXTTOUNICODE_INFO_ERROR) == 0;
+}
+
+void rtl_uString_newReplaceFirst(
+ rtl_uString ** newStr, rtl_uString * str, rtl_uString const * from,
+ rtl_uString const * to, sal_Int32 * index) SAL_THROW_EXTERN_C()
+{
+ assert(from != nullptr);
+ assert(to != nullptr);
+ rtl_uString_newReplaceFirstUtf16LUtf16L(newStr, str, from->buffer, from->length, to->buffer,
+ to->length, index);
+}
+
+void rtl_uString_newReplaceFirstAsciiL(
+ rtl_uString ** newStr, rtl_uString * str, char const * from,
+ sal_Int32 fromLength, rtl_uString const * to, sal_Int32 * index)
+ SAL_THROW_EXTERN_C()
+{
+ assert(to != nullptr);
+ rtl_uString_newReplaceFirstAsciiLUtf16L(newStr, str, from, fromLength, to->buffer, to->length,
+ index);
+}
+
+void rtl_uString_newReplaceFirstToAsciiL(
+ rtl_uString ** newStr, rtl_uString * str, rtl_uString const * from,
+ char const * to, sal_Int32 toLength, sal_Int32 * index)
+ SAL_THROW_EXTERN_C()
+{
+ assert(from != nullptr);
+ rtl_uString_newReplaceFirstUtf16LAsciiL(newStr, str, from->buffer, from->length, to, toLength,
+ index);
+}
+
+void rtl_uString_newReplaceFirstAsciiLAsciiL(
+ rtl_uString ** newStr, rtl_uString * str, char const * from,
+ sal_Int32 fromLength, char const * to, sal_Int32 toLength,
+ sal_Int32 * index) SAL_THROW_EXTERN_C()
+{
+ assert(index != nullptr);
+ rtl::str::newReplaceFirst(newStr, str, from, fromLength, to, toLength, *index);
+}
+
+void rtl_uString_newReplaceFirstAsciiLUtf16L(
+ rtl_uString ** newStr, rtl_uString * str, char const * from,
+ sal_Int32 fromLength, sal_Unicode const * to, sal_Int32 toLength,
+ sal_Int32 * index) SAL_THROW_EXTERN_C()
+{
+ assert(index != nullptr);
+ rtl::str::newReplaceFirst(newStr, str, from, fromLength, to, toLength, *index);
+}
+
+void rtl_uString_newReplaceFirstUtf16LAsciiL(
+ rtl_uString ** newStr, rtl_uString * str, sal_Unicode const * from,
+ sal_Int32 fromLength, char const * to, sal_Int32 toLength,
+ sal_Int32 * index) SAL_THROW_EXTERN_C()
+{
+ assert(index != nullptr);
+ rtl::str::newReplaceFirst(newStr, str, from, fromLength, to, toLength, *index);
+}
+
+void rtl_uString_newReplaceFirstUtf16LUtf16L(
+ rtl_uString ** newStr, rtl_uString * str, sal_Unicode const * from,
+ sal_Int32 fromLength, sal_Unicode const * to, sal_Int32 toLength,
+ sal_Int32 * index) SAL_THROW_EXTERN_C()
+{
+ assert(index != nullptr);
+ rtl::str::newReplaceFirst(newStr, str, from, fromLength, to, toLength, *index);
+}
+
+void rtl_uString_newReplaceAll(
+ rtl_uString ** newStr, rtl_uString * str, rtl_uString const * from,
+ rtl_uString const * to) SAL_THROW_EXTERN_C()
+{
+ rtl_uString_newReplaceAllFromIndex( newStr, str, from, to, 0 );
+}
+
+void rtl_uString_newReplaceAllFromIndex(
+ rtl_uString ** newStr, rtl_uString * str, rtl_uString const * from,
+ rtl_uString const * to, sal_Int32 fromIndex) SAL_THROW_EXTERN_C()
+{
+ assert(to != nullptr);
+ assert(fromIndex >= 0 && fromIndex <= str->length);
+ rtl_uString_newReplaceAllFromIndexUtf16LUtf16L(newStr, str, from->buffer, from->length,
+ to->buffer, to->length, fromIndex);
+}
+
+void rtl_uString_newReplaceAllAsciiL(
+ rtl_uString ** newStr, rtl_uString * str, char const * from,
+ sal_Int32 fromLength, rtl_uString const * to) SAL_THROW_EXTERN_C()
+{
+ assert(to != nullptr);
+ rtl_uString_newReplaceAllAsciiLUtf16L(newStr, str, from, fromLength, to->buffer, to->length);
+}
+
+void rtl_uString_newReplaceAllToAsciiL(
+ rtl_uString ** newStr, rtl_uString * str, rtl_uString const * from,
+ char const * to, sal_Int32 toLength) SAL_THROW_EXTERN_C()
+{
+ assert(from != nullptr);
+ rtl_uString_newReplaceAllUtf16LAsciiL(newStr, str, from->buffer, from->length, to, toLength);
+}
+
+void rtl_uString_newReplaceAllAsciiLAsciiL(
+ rtl_uString ** newStr, rtl_uString * str, char const * from,
+ sal_Int32 fromLength, char const * to, sal_Int32 toLength)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceAllFromIndex(newStr, str, from, fromLength, to, toLength, 0);
+}
+
+void rtl_uString_newReplaceAllAsciiLUtf16L(
+ rtl_uString ** newStr, rtl_uString * str, char const * from,
+ sal_Int32 fromLength, sal_Unicode const * to, sal_Int32 toLength)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceAllFromIndex(newStr, str, from, fromLength, to, toLength, 0);
+}
+
+void rtl_uString_newReplaceAllUtf16LAsciiL(
+ rtl_uString ** newStr, rtl_uString * str, sal_Unicode const * from,
+ sal_Int32 fromLength, char const * to, sal_Int32 toLength)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceAllFromIndex(newStr, str, from, fromLength, to, toLength, 0);
+}
+
+void rtl_uString_newReplaceAllUtf16LUtf16L(
+ rtl_uString ** newStr, rtl_uString * str, sal_Unicode const * from,
+ sal_Int32 fromLength, sal_Unicode const * to, sal_Int32 toLength)
+ SAL_THROW_EXTERN_C()
+{
+ rtl_uString_newReplaceAllFromIndexUtf16LUtf16L(newStr, str, from, fromLength, to, toLength, 0);
+}
+
+void rtl_uString_newReplaceAllFromIndexUtf16LUtf16L(
+ rtl_uString ** newStr, rtl_uString * str, sal_Unicode const * from,
+ sal_Int32 fromLength, sal_Unicode const * to, sal_Int32 toLength, sal_Int32 fromIndex)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceAllFromIndex(newStr, str, from, fromLength, to, toLength, fromIndex);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_getLength(const sal_Unicode* pStr) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::getLength(pStr);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_compare(const sal_Unicode* pStr1, const sal_Unicode* pStr2)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::null_terminated(pStr1), rtl::str::null_terminated(pStr2),
+ rtl::str::CompareNormal(), rtl::str::noShortening);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_compare_WithLength(const sal_Unicode* pStr1, sal_Int32 nStr1Len,
+ const sal_Unicode* pStr2, sal_Int32 nStr2Len)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len),
+ rtl::str::with_length(pStr2, nStr2Len), rtl::str::CompareNormal(),
+ rtl::str::noShortening);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_shortenedCompare_WithLength(
+ const sal_Unicode* pStr1, sal_Int32 nStr1Len, const sal_Unicode* pStr2, sal_Int32 nStr2Len,
+ sal_Int32 nShortenedLength) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len),
+ rtl::str::with_length(pStr2, nStr2Len), rtl::str::CompareNormal(),
+ nShortenedLength);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_reverseCompare_WithLength(const sal_Unicode* pStr1, sal_Int32 nStr1Len,
+ const sal_Unicode* pStr2, sal_Int32 nStr2Len)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::reverseCompare_WithLengths(pStr1, nStr1Len, pStr2, nStr2Len,
+ rtl::str::CompareNormal());
+}
+
+sal_Int32 SAL_CALL rtl_ustr_compareIgnoreAsciiCase(const sal_Unicode* pStr1,
+ const sal_Unicode* pStr2) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::null_terminated(pStr1), rtl::str::null_terminated(pStr2),
+ rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_compareIgnoreAsciiCase_WithLength(const sal_Unicode* pStr1,
+ sal_Int32 nStr1Len,
+ const sal_Unicode* pStr2,
+ sal_Int32 nStr2Len)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len),
+ rtl::str::with_length(pStr2, nStr2Len),
+ rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength(
+ const sal_Unicode* pStr1, sal_Int32 nStr1Len, const sal_Unicode* pStr2, sal_Int32 nStr2Len,
+ sal_Int32 nShortenedLength) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len),
+ rtl::str::with_length(pStr2, nStr2Len),
+ rtl::str::CompareIgnoreAsciiCase(), nShortenedLength);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_hashCode(const sal_Unicode* pStr) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::hashCode(pStr);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_hashCode_WithLength(const sal_Unicode* pStr, sal_Int32 nLen)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::hashCode_WithLength(pStr, nLen);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_indexOfChar(const sal_Unicode* pStr, sal_Unicode c) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::indexOfChar(pStr, c);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_indexOfChar_WithLength(const sal_Unicode* pStr, sal_Int32 nLen,
+ sal_Unicode c) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::indexOfChar_WithLength(pStr, nLen, c);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_lastIndexOfChar(const sal_Unicode* pStr, sal_Unicode c)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::lastIndexOfChar(pStr, c);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_lastIndexOfChar_WithLength(const sal_Unicode* pStr, sal_Int32 nLen,
+ sal_Unicode c) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::lastIndexOfChar_WithLength(pStr, nLen, c);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_indexOfStr(const sal_Unicode* pStr, const sal_Unicode* pSubStr)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::indexOfStr(pStr, pSubStr);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_indexOfStr_WithLength(const sal_Unicode* pStr, sal_Int32 nStrLen,
+ const sal_Unicode* pSubStr, sal_Int32 nSubLen)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::indexOfStr_WithLength(pStr, nStrLen, pSubStr, nSubLen);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_lastIndexOfStr(const sal_Unicode* pStr, const sal_Unicode* pSubStr)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::lastIndexOfStr(pStr, pSubStr);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_lastIndexOfStr_WithLength(const sal_Unicode* pStr, sal_Int32 nStrLen,
+ const sal_Unicode* pSubStr, sal_Int32 nSubLen)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::lastIndexOfStr_WithLength(pStr, nStrLen, pSubStr, nSubLen);
+}
+
+void SAL_CALL rtl_ustr_replaceChar(sal_Unicode* pStr, sal_Unicode cOld, sal_Unicode cNew)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::replaceChars(rtl::str::null_terminated(pStr), rtl::str::FromTo(cOld, cNew));
+}
+
+void SAL_CALL rtl_ustr_replaceChar_WithLength(sal_Unicode* pStr, sal_Int32 nLen, sal_Unicode cOld,
+ sal_Unicode cNew) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::replaceChars(rtl::str::with_length(pStr, nLen), rtl::str::FromTo(cOld, cNew));
+}
+
+void SAL_CALL rtl_ustr_toAsciiLowerCase(sal_Unicode* pStr) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::replaceChars(rtl::str::null_terminated(pStr), rtl::str::toAsciiLower);
+}
+
+void SAL_CALL rtl_ustr_toAsciiLowerCase_WithLength(sal_Unicode* pStr, sal_Int32 nLen)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::replaceChars(rtl::str::with_length(pStr, nLen), rtl::str::toAsciiLower);
+}
+
+void SAL_CALL rtl_ustr_toAsciiUpperCase(sal_Unicode* pStr) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::replaceChars(rtl::str::null_terminated(pStr), rtl::str::toAsciiUpper);
+}
+
+void SAL_CALL rtl_ustr_toAsciiUpperCase_WithLength(sal_Unicode* pStr, sal_Int32 nLen)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::replaceChars(rtl::str::with_length(pStr, nLen), rtl::str::toAsciiUpper);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_trim(sal_Unicode* pStr) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::trim(pStr);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_trim_WithLength(sal_Unicode* pStr, sal_Int32 nLen) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::trim_WithLength(pStr, nLen);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_valueOfBoolean(sal_Unicode* pStr, sal_Bool b) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfBoolean(pStr, b);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_valueOfChar(sal_Unicode* pStr, sal_Unicode c) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfChar(pStr, c);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_valueOfInt32(sal_Unicode* pStr, sal_Int32 n, sal_Int16 nRadix)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfInt<RTL_USTR_MAX_VALUEOFINT32>(pStr, n, nRadix);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_valueOfInt64(sal_Unicode* pStr, sal_Int64 n, sal_Int16 nRadix)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfInt<RTL_USTR_MAX_VALUEOFINT64>(pStr, n, nRadix);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_valueOfUInt64(sal_Unicode* pStr, sal_uInt64 n, sal_Int16 nRadix)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::valueOfInt<RTL_USTR_MAX_VALUEOFUINT64>(pStr, n, nRadix);
+}
+
+sal_Bool SAL_CALL rtl_ustr_toBoolean(const sal_Unicode* pStr) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::toBoolean(pStr);
+}
+
+sal_Int32 SAL_CALL rtl_ustr_toInt32(const sal_Unicode* pStr, sal_Int16 nRadix) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::toInt<sal_Int32>(rtl::str::null_terminated(pStr), nRadix);
+}
+
+sal_Int64 SAL_CALL rtl_ustr_toInt64(const sal_Unicode* pStr, sal_Int16 nRadix) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::toInt<sal_Int64>(rtl::str::null_terminated(pStr), nRadix);
+}
+
+sal_Int64 SAL_CALL rtl_ustr_toInt64_WithLength(const sal_Unicode* pStr, sal_Int16 nRadix,
+ sal_Int32 nStrLength) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::toInt<sal_Int64>(rtl::str::with_length(pStr, nStrLength), nRadix);
+}
+
+sal_uInt32 SAL_CALL rtl_ustr_toUInt32(const sal_Unicode* pStr, sal_Int16 nRadix)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::toInt<sal_uInt32>(rtl::str::null_terminated(pStr), nRadix);
+}
+
+sal_uInt64 SAL_CALL rtl_ustr_toUInt64(const sal_Unicode* pStr, sal_Int16 nRadix)
+ SAL_THROW_EXTERN_C()
+{
+ return rtl::str::toInt<sal_uInt64>(rtl::str::null_terminated(pStr), nRadix);
+}
+
+rtl_uString* rtl_uString_ImplAlloc(sal_Int32 nLen)
+{
+ return rtl::str::Alloc<rtl_uString>(nLen);
+}
+
+void SAL_CALL rtl_uString_acquire(rtl_uString* pThis) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::acquire(pThis);
+}
+
+void SAL_CALL rtl_uString_release(rtl_uString* pThis) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::release(pThis);
+}
+
+void SAL_CALL rtl_uString_new(rtl_uString** ppThis) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::new_(ppThis);
+}
+
+rtl_uString* SAL_CALL rtl_uString_alloc(sal_Int32 nLen) SAL_THROW_EXTERN_C()
+{
+ assert(nLen >= 0);
+ return rtl::str::Alloc<rtl_uString>(nLen);
+}
+
+void SAL_CALL rtl_uString_new_WithLength(rtl_uString** ppThis, sal_Int32 nLen) SAL_THROW_EXTERN_C()
+{
+ rtl::str::new_WithLength(ppThis, nLen);
+}
+
+void SAL_CALL rtl_uString_newFromString(rtl_uString** ppThis, const rtl_uString* pStr)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newFromString(ppThis, pStr);
+}
+
+void SAL_CALL rtl_uString_newFromStr(rtl_uString** ppThis, const sal_Unicode* pCharStr)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newFromStr(ppThis, pCharStr);
+}
+
+void SAL_CALL rtl_uString_newFromStr_WithLength(rtl_uString** ppThis, const sal_Unicode* pCharStr,
+ sal_Int32 nLen) SAL_THROW_EXTERN_C()
+{
+ rtl::str::newFromStr_WithLength(ppThis, pCharStr, nLen);
+}
+
+void SAL_CALL rtl_uString_newFromSubString(rtl_uString** ppThis, const rtl_uString* pFrom,
+ sal_Int32 beginIndex, sal_Int32 count)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newFromSubString(ppThis, pFrom, beginIndex, count);
+}
+
+// Used when creating from string literals.
+void SAL_CALL rtl_uString_newFromLiteral(rtl_uString** ppThis, const char* pCharStr, sal_Int32 nLen,
+ sal_Int32 allocExtra) SAL_THROW_EXTERN_C()
+{
+ rtl::str::newFromStr_WithLength(ppThis, pCharStr, nLen, allocExtra);
+}
+
+void SAL_CALL rtl_uString_assign(rtl_uString** ppThis, rtl_uString* pStr) SAL_THROW_EXTERN_C()
+{
+ rtl::str::assign(ppThis, pStr);
+}
+
+sal_Int32 SAL_CALL rtl_uString_getLength(const rtl_uString* pThis) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::getLength(pThis);
+}
+
+sal_Unicode* SAL_CALL rtl_uString_getStr(rtl_uString* pThis) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::getStr(pThis);
+}
+
+void SAL_CALL rtl_uString_newConcat(rtl_uString** ppThis, rtl_uString* pLeft, rtl_uString* pRight)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newConcat(ppThis, pLeft, pRight);
+}
+
+void SAL_CALL rtl_uString_ensureCapacity(rtl_uString** ppThis, sal_Int32 size) SAL_THROW_EXTERN_C()
+{
+ rtl::str::ensureCapacity(ppThis, size);
+}
+
+void SAL_CALL rtl_uString_newReplaceStrAt(rtl_uString** ppThis, rtl_uString* pStr, sal_Int32 nIndex,
+ sal_Int32 nCount, rtl_uString* pNewSubStr)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceStrAt(ppThis, pStr, nIndex, nCount, pNewSubStr);
+}
+
+void SAL_CALL rtl_uString_newReplaceStrAtUtf16L(rtl_uString** ppThis, rtl_uString* pStr, sal_Int32 nIndex,
+ sal_Int32 nCount, sal_Unicode const * subStr, sal_Int32 substrLen)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceStrAt(ppThis, pStr, nIndex, nCount, subStr, substrLen);
+}
+
+void SAL_CALL rtl_uString_newReplace(rtl_uString** ppThis, rtl_uString* pStr, sal_Unicode cOld,
+ sal_Unicode cNew) SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplace(ppThis, pStr, cOld, cNew);
+}
+
+void SAL_CALL rtl_uString_newToAsciiLowerCase(rtl_uString** ppThis, rtl_uString* pStr)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceChars<rtl::str::ToAsciiLower>(ppThis, pStr);
+}
+
+void SAL_CALL rtl_uString_newToAsciiUpperCase(rtl_uString** ppThis, rtl_uString* pStr)
+ SAL_THROW_EXTERN_C()
+{
+ rtl::str::newReplaceChars<rtl::str::ToAsciiUpper>(ppThis, pStr);
+}
+
+void SAL_CALL rtl_uString_newTrim(rtl_uString** ppThis, rtl_uString* pStr) SAL_THROW_EXTERN_C()
+{
+ rtl::str::newTrim(ppThis, pStr);
+}
+
+sal_Int32 SAL_CALL rtl_uString_getToken(rtl_uString** ppThis, rtl_uString* pStr, sal_Int32 nToken,
+ sal_Unicode cTok, sal_Int32 nIndex) SAL_THROW_EXTERN_C()
+{
+ return rtl::str::getToken(ppThis, pStr, nToken, cTok, nIndex);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sal/rtl/uuid.cxx b/sal/rtl/uuid.cxx
new file mode 100644
index 000000000..130be1245
--- /dev/null
+++ b/sal/rtl/uuid.cxx
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <osl/mutex.hxx>
+#include <rtl/random.h>
+#include <rtl/uuid.h>
+#include <rtl/digest.h>
+
+#define SWAP_INT32_TO_NETWORK(x)\
+ { sal_uInt32 y = x;\
+ sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(&(x)); \
+ p[0] = static_cast<sal_uInt8>( ( y >> 24 ) & 0xff );\
+ p[1] = static_cast<sal_uInt8>( ( y >> 16 ) & 0xff );\
+ p[2] = static_cast<sal_uInt8>( ( y >> 8 ) & 0xff );\
+ p[3] = static_cast<sal_uInt8>( ( y ) & 0xff);\
+ }
+#define SWAP_INT16_TO_NETWORK(x)\
+ { sal_uInt16 y = x;\
+ sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(&(x)); \
+ p[0] = static_cast<sal_uInt8>( ( y >> 8 ) & 0xff );\
+ p[1] = static_cast<sal_uInt8>( ( y ) & 0xff);\
+ }
+
+#define SWAP_NETWORK_TO_INT16(x)\
+ { sal_uInt16 y = x;\
+ sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(&(y));\
+ x = ( ( (static_cast<sal_uInt16>(p[0])) & 0xff) << 8 ) |\
+ ( ( static_cast<sal_uInt16>(p[1])) & 0xff);\
+ }
+#define SWAP_NETWORK_TO_INT32(x)\
+ { sal_uInt32 y = x;\
+ sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(&(y)); \
+ x = ( ( (static_cast<sal_uInt32>(p[0])) & 0xff) << 24 ) |\
+ ( ( (static_cast<sal_uInt32>(p[1])) & 0xff) << 16 ) |\
+ ( ( (static_cast<sal_uInt32>(p[2])) & 0xff) << 8 ) |\
+ ( ( static_cast<sal_uInt32>(p[3])) & 0xff);\
+ }
+
+namespace {
+
+struct UUID
+{
+ sal_uInt32 time_low;
+ sal_uInt16 time_mid;
+ sal_uInt16 time_hi_and_version;
+ sal_uInt8 clock_seq_hi_and_reserved;
+ sal_uInt8 clock_seq_low;
+ sal_uInt8 node[6];
+};
+
+}
+
+static void write_v3( sal_uInt8 *pUuid )
+{
+ UUID uuid;
+ // copy to avoid alignment problems
+ memcpy(&uuid, pUuid, 16);
+
+ SWAP_NETWORK_TO_INT32(uuid.time_low);
+ SWAP_NETWORK_TO_INT16(uuid.time_mid);
+ SWAP_NETWORK_TO_INT16(uuid.time_hi_and_version);
+
+ /* put in the variant and version bits */
+ uuid.time_hi_and_version &= 0x0FFF;
+ uuid.time_hi_and_version |= (3 << 12);
+ uuid.clock_seq_hi_and_reserved &= 0x3F;
+ uuid.clock_seq_hi_and_reserved |= 0x80;
+
+ SWAP_INT32_TO_NETWORK(uuid.time_low);
+ SWAP_INT16_TO_NETWORK(uuid.time_mid);
+ SWAP_INT16_TO_NETWORK(uuid.time_hi_and_version);
+
+ memcpy(pUuid, &uuid, 16);
+}
+
+extern "C" void SAL_CALL rtl_createUuid(sal_uInt8 *pTargetUUID ,
+ SAL_UNUSED_PARAMETER const sal_uInt8 *,
+ SAL_UNUSED_PARAMETER sal_Bool)
+{
+ {
+ static rtlRandomPool pool = []() {
+ rtlRandomPool aPool = rtl_random_createPool();
+ if (!aPool)
+ {
+ abort();
+ // only possible way to signal failure here (rtl_createUuid
+ // being part of a fixed C API)
+ }
+ return aPool;
+ }();
+
+ osl::MutexGuard g(osl::Mutex::getGlobalMutex());
+ if (rtl_random_getBytes(pool, pTargetUUID, 16) != rtl_Random_E_None)
+ {
+ abort();
+ // only possible way to signal failure here (rtl_createUuid
+ // being part of a fixed C API)
+ }
+ }
+ // See ITU-T Recommendation X.667:
+ pTargetUUID[6] &= 0x0F;
+ pTargetUUID[6] |= 0x40;
+ pTargetUUID[8] &= 0x3F;
+ pTargetUUID[8] |= 0x80;
+}
+
+extern "C" void SAL_CALL rtl_createNamedUuid(sal_uInt8 *pTargetUUID,
+ const sal_uInt8 *pNameSpaceUUID,
+ const rtl_String *pName )
+{
+ rtlDigest digest = rtl_digest_createMD5();
+
+ rtl_digest_updateMD5(digest, pNameSpaceUUID, 16);
+ rtl_digest_updateMD5(digest, pName->buffer, pName->length);
+
+ rtl_digest_getMD5(digest, pTargetUUID, 16);
+ rtl_digest_destroyMD5(digest);
+
+ write_v3(pTargetUUID);
+}
+
+extern "C" sal_Int32 SAL_CALL rtl_compareUuid(const sal_uInt8 *pUUID1, const sal_uInt8 *pUUID2)
+{
+ int i;
+ UUID u1;
+ UUID u2;
+ memcpy(&u1, pUUID1, 16 );
+ memcpy(&u2, pUUID2, 16 );
+
+ SWAP_NETWORK_TO_INT32(u1.time_low);
+ SWAP_NETWORK_TO_INT16(u1.time_mid);
+ SWAP_NETWORK_TO_INT16(u1.time_hi_and_version);
+
+ SWAP_NETWORK_TO_INT32(u2.time_low);
+ SWAP_NETWORK_TO_INT16(u2.time_mid);
+ SWAP_NETWORK_TO_INT16(u2.time_hi_and_version);
+
+#define CHECK(f1, f2) if (f1 != f2) return f1 < f2 ? -1 : 1;
+ CHECK(u1.time_low, u2.time_low);
+ CHECK(u1.time_mid, u2.time_mid);
+ CHECK(u1.time_hi_and_version, u2.time_hi_and_version);
+ CHECK(u1.clock_seq_hi_and_reserved, u2.clock_seq_hi_and_reserved);
+ CHECK(u1.clock_seq_low, u2.clock_seq_low);
+ for (i = 0; i < 6; i++)
+ {
+ if (u1.node[i] < u2.node[i])
+ return -1;
+ if (u1.node[i] > u2.node[i])
+ return 1;
+ }
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */