diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /sal/rtl | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | sal/rtl/alloc_arena.cxx | 1112 | ||||
-rw-r--r-- | sal/rtl/alloc_arena.hxx | 118 | ||||
-rw-r--r-- | sal/rtl/alloc_cache.cxx | 225 | ||||
-rw-r--r-- | sal/rtl/alloc_cache.hxx | 40 | ||||
-rw-r--r-- | sal/rtl/alloc_fini.cxx | 90 | ||||
-rw-r--r-- | sal/rtl/alloc_global.cxx | 81 | ||||
-rw-r--r-- | sal/rtl/alloc_impl.hxx | 217 | ||||
-rw-r--r-- | sal/rtl/bootstrap.cxx | 1014 | ||||
-rw-r--r-- | sal/rtl/byteseq.cxx | 245 | ||||
-rw-r--r-- | sal/rtl/cipher.cxx | 1433 | ||||
-rw-r--r-- | sal/rtl/cmdargs.cxx | 101 | ||||
-rw-r--r-- | sal/rtl/crc.cxx | 149 | ||||
-rw-r--r-- | sal/rtl/digest.cxx | 1897 | ||||
-rw-r--r-- | sal/rtl/hash.cxx | 241 | ||||
-rw-r--r-- | sal/rtl/hash.hxx | 33 | ||||
-rw-r--r-- | sal/rtl/locale.cxx | 142 | ||||
-rw-r--r-- | sal/rtl/math.cxx | 1410 | ||||
-rw-r--r-- | sal/rtl/random.cxx | 310 | ||||
-rw-r--r-- | sal/rtl/rtl_process.cxx | 58 | ||||
-rw-r--r-- | sal/rtl/strbuf.cxx | 182 | ||||
-rw-r--r-- | sal/rtl/strimp.cxx | 120 | ||||
-rw-r--r-- | sal/rtl/strimp.hxx | 96 | ||||
-rw-r--r-- | sal/rtl/string.cxx | 394 | ||||
-rw-r--r-- | sal/rtl/strtmpl.cxx | 1911 | ||||
-rw-r--r-- | sal/rtl/unload.cxx | 35 | ||||
-rw-r--r-- | sal/rtl/uri.cxx | 893 | ||||
-rw-r--r-- | sal/rtl/ustrbuf.cxx | 268 | ||||
-rw-r--r-- | sal/rtl/ustring.cxx | 1562 | ||||
-rw-r--r-- | sal/rtl/uuid.cxx | 173 |
29 files changed, 14550 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..3745e900c --- /dev/null +++ b/sal/rtl/alloc_fini.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 <rtl/instance.hxx> + +#include <rtllifecycle.h> + +namespace +{ + struct rtlCacheSingleton + { + rtlCacheSingleton() + { + rtl_cache_init(); + } + ~rtlCacheSingleton() + { + rtl_cache_fini(); + } + }; + class theCacheSingleton + : public rtl::Static<rtlCacheSingleton, theCacheSingleton>{}; +} + +void ensureCacheSingleton() +{ + theCacheSingleton::get(); +} + +namespace +{ + struct rtlArenaSingleton + { + rtlArenaSingleton() + { + rtl_arena_init(); + } + ~rtlArenaSingleton() + { + rtl_arena_fini(); + } + }; + class theArenaSingleton + : public rtl::Static<rtlArenaSingleton, theArenaSingleton>{}; +} + +void ensureArenaSingleton() +{ + theArenaSingleton::get(); +} + +namespace +{ + struct rtlLocaleSingleton + { + rtlLocaleSingleton() + { + rtl_locale_init(); + } + ~rtlLocaleSingleton() + { + rtl_locale_fini(); + } + }; + class theLocaleSingleton + : public rtl::Static<rtlLocaleSingleton, theLocaleSingleton>{}; +} + +void ensureLocaleSingleton() +{ + theLocaleSingleton::get(); +} + +/* 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..9ceba4f41 --- /dev/null +++ b/sal/rtl/alloc_global.cxx @@ -0,0 +1,81 @@ +/* -*- 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() +{ + void * p = rtl_allocateMemory (n); + if (p) + memset (p, 0, n); + return p; +} + +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..bdacba04a --- /dev/null +++ b/sal/rtl/alloc_impl.hxx @@ -0,0 +1,217 @@ +/* -*- 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 SAL_TYPES_SIZEOFLONG == 8 + if (n & 0xffffffff00000000ul) + { + k |= 32; + n >>= 32; + } +#endif + 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 SAL_TYPES_SIZEOFLONG == 8 + if (!(n & 0xffffffff)) + { + k |= 32; + n >>= 32; + } +#endif + + 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..460786c77 --- /dev/null +++ b/sal/rtl/bootstrap.cxx @@ -0,0 +1,1014 @@ +/* -*- 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/instance.hxx> +#include <rtl/malformeduriexception.hxx> +#include <rtl/uri.hxx> +#include <sal/log.hxx> + +#include <vector> +#include <algorithm> +#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; + +static char const VND_SUN_STAR_PATHNAME[] = "vnd.sun.star.pathname:"; + +bool isPathnameUrl(OUString const & url) +{ + return url.matchIgnoreAsciiCase(VND_SUN_STAR_PATHNAME); +} + +bool resolvePathnameUrl(OUString * url) +{ + OSL_ASSERT(url); + if (!isPathnameUrl(*url) || + (osl::FileBase::getFileURLFromSystemPath( + url->copy(RTL_CONSTASCII_LENGTH(VND_SUN_STAR_PATHNAME)), *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, OUString const & text, LookupMode mode, + ExpandRequestLink const * requestStack); + +OUString recursivelyExpandMacros( + Bootstrap_Impl const * file, OUString const & 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 const & name, OUString const & value ) + : sName( name ), + sValue( 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 +{ + struct rtl_bootstrap_set_vector : + public rtl::Static< 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) + { + rtl_uString *pArg = nullptr; + osl_getCommandArg( i, &pArg ); + if( (pArg->buffer[0] == '-' || pArg->buffer[0] == '/' ) && + pArg->buffer[1] == 'e' && + pArg->buffer[2] == 'n' && + pArg->buffer[3] == 'v' && + pArg->buffer[4] == ':' ) + { + sal_Int32 nIndex = rtl_ustr_indexOfChar( pArg->buffer, '=' ); + + if( nIndex >= 0 ) + { + rtl_bootstrap_NameValue nameValue; + nameValue.sName = OUString( &(pArg->buffer[5]), nIndex - 5 ); + nameValue.sValue = OUString( &(pArg->buffer[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 ); + } + } + rtl_uString_release( pArg ); + }; + 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); +#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() + && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(progExt)) + { + fileName = fileName.copy(0, fileName.getLength() - progExt.getLength()); + } + + progExt = ".exe"; + if (fileName.getLength() > progExt.getLength() + && fileName.copy(fileName.getLength() - progExt.getLength()).equalsIgnoreAsciiCase(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"), 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(line.copy(0,nIndex).trim(), RTL_TEXTENCODING_ASCII_US); + nameValue.sValue = OStringToOUString(line.copy(nIndex+1).trim(), 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; +}; + +struct FundamentalIni: public rtl::Static< FundamentalIniData, FundamentalIni > +{}; + +} + +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::get(); + 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::get(), 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 { + +struct bootstrap_map { + typedef std::unordered_map< + OUString, Bootstrap_Impl * > t; + + bootstrap_map(const bootstrap_map&) = delete; + bootstrap_map& operator=(const bootstrap_map&) = delete; + + // get and release must only be called properly synchronized via some mutex + // (e.g., osl::Mutex::getGlobalMutex()): + + static t * get() + { + if (!m_map) + m_map = new t; + + return m_map; + } + + static void release() + { + if (m_map != nullptr && m_map->empty()) + { + delete m_map; + m_map = nullptr; + } + } + +private: + static t * m_map; +}; + +bootstrap_map::t * bootstrap_map::m_map = nullptr; + +} + +rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open(rtl_uString * pIniName) +{ + OUString iniName( pIniName ); + + // normalize path + FileStatus status(osl_FileStatus_Mask_FileURL); + DirectoryItem dirItem; + if (DirectoryItem::get(iniName, dirItem) != DirectoryItem::E_None || + dirItem.getFileStatus(status) != DirectoryItem::E_None) + { + return nullptr; + } + + iniName = status.getFileURL(); + + Bootstrap_Impl * that; + osl::ResettableMutexGuard guard(osl::Mutex::getGlobalMutex()); + bootstrap_map::t* p_bootstrap_map = bootstrap_map::get(); + bootstrap_map::t::const_iterator iFind(p_bootstrap_map->find(iniName)); + if (iFind == p_bootstrap_map->end()) + { + bootstrap_map::release(); + guard.clear(); + that = new Bootstrap_Impl(iniName); + guard.reset(); + p_bootstrap_map = bootstrap_map::get(); + iFind = p_bootstrap_map->find(iniName); + if (iFind == p_bootstrap_map->end()) + { + ++that->_nRefCount; + ::std::pair< bootstrap_map::t::iterator, bool > insertion( + p_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()); + bootstrap_map::t* p_bootstrap_map = bootstrap_map::get(); + OSL_ASSERT(p_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 (p_bootstrap_map->size() > nLeaking) + { + ::std::size_t erased = p_bootstrap_map->erase( that->_iniName ); + if (erased != 1) { + OSL_ASSERT( false ); + } + delete that; + } + bootstrap_map::release(); +} + +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()); + + NameValueVector& r_rtl_bootstrap_set_vector= rtl_bootstrap_set_vector::get(); + for (auto & item : r_rtl_bootstrap_set_vector) + { + if (item.sName == name) + { + item.sValue = value; + return; + } + } + + SAL_INFO("sal.bootstrap", "explicitly getting: name=" << name << " value=" <<value); + + r_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(OUString const & text, sal_Int32 * pos, bool * escaped) +{ + OSL_ASSERT(pos && *pos >= 0 && *pos < text.getLength() && escaped); + sal_Unicode c = text[(*pos)++]; + if (c == '\\') + { + int n1, n2, n3, n4; + if (*pos < text.getLength() - 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.getLength()) + { + *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, OUString const & text, LookupMode mode, + ExpandRequestLink const * requestStack) +{ + SAL_INFO("sal.bootstrap", "expandMacros called with: " << text); + OUStringBuffer buf(2048); + + for (sal_Int32 i = 0; i < text.getLength();) + { + bool escaped; + sal_Unicode c = read(text, &i, &escaped); + if (escaped || c != '$') + { + buf.append(c); + } + else + { + if (i < text.getLength() && text[i] == '{') + { + ++i; + sal_Int32 p = i; + sal_Int32 nesting = 0; + OUString seg[3]; + int n = 0; + + while (i < text.getLength()) + { + sal_Int32 j = i; + c = read(text, &i, &escaped); + + if (!escaped) + { + switch (c) + { + case '{': + ++nesting; + break; + case '}': + if (nesting == 0) + { + seg[n++] = text.copy(p, j - p); + goto done; + } + else + { + --nesting; + } + break; + case ':': + if (nesting == 0 && n < 2) + { + seg[n++] = text.copy(p, j - p); + p = i; + } + break; + } + } + } + done: + for (int j = 0; j < n; ++j) + { + seg[j] = expandMacros(file, seg[j], mode, requestStack); + } + + if (n == 1) + { + buf.append(lookup(file, mode, false, seg[0], 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 + { + if (n == 3 && seg[1].isEmpty()) + { + // For backward compatibility, treat ${file::key} the + // same as just ${file:key}: + seg[1] = seg[2]; + n = 2; + } + + if (n == 2) + { + buf.append( + lookup( + static_cast< Bootstrap_Impl * >( + rtl::Bootstrap(seg[0]).getHandle()), + mode, false, seg[1], 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(text.getLength()); + for (; i < text.getLength();) + { + sal_Int32 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..c78a1e22f --- /dev/null +++ b/sal/rtl/byteseq.cxx @@ -0,0 +1,245 @@ +/* -*- 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, * pNew; + + OSL_ENSURE( ppSequence, "### null ptr!" ); + pSequence = *ppSequence; + + if (pSequence->nRefCount > 1) + { + 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, * pNew; + sal_Int32 nElements; + + assert(ppSequence && "### null ptr!"); + pSequence = *ppSequence; + nElements = pSequence->nElements; + + if (nElements == nSize) + return; + + if (pSequence->nRefCount > 1) // split + { + 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..344286549 --- /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); + +static 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..8eb328258 --- /dev/null +++ b/sal/rtl/cmdargs.cxx @@ -0,0 +1,101 @@ +/* -*- 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); + if ((pArg->buffer[0] == '-' || pArg->buffer[0] == '/') && + pArg->buffer[1] == 'e' && + pArg->buffer[2] == 'n' && + pArg->buffer[3] == 'v' && + pArg->buffer[4] == ':' && + rtl_ustr_indexOfChar (&(pArg->buffer[5]), '=') >= 0 ) + { + // 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..479281e76 --- /dev/null +++ b/sal/rtl/crc.cxx @@ -0,0 +1,149 @@ +/* -*- 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> + +/** + rtl_crc32Table (CRC polynomial 0xEDB88320). +*/ +static const sal_uInt32 rtl_crc32Table[256] = +{ + /* 0 */ + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + + /* 1 */ + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + + /* 2 */ + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + + /* 3 */ + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + + /* 4 */ + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + + /* 5 */ + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + + /* 6 */ + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + + /* 7 */ + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + + /* 8 */ + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + + /* 9 */ + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + + /* A */ + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + + /* B */ + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + + /* C */ + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + + /* D */ + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + + /* E */ + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + + /* F */ + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +#define UPDCRC32(crc, octet) \ + (rtl_crc32Table[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8)) + +sal_uInt32 SAL_CALL rtl_crc32 ( + sal_uInt32 Crc, + const void *Data, sal_uInt32 DatLen) SAL_THROW_EXTERN_C() +{ + if (Data) + { + const sal_uInt8 *p = static_cast<const sal_uInt8 *>(Data); + const sal_uInt8 *q = p + DatLen; + + Crc = ~Crc; + + while (p < q) + { + Crc = UPDCRC32(Crc, *(p++)); + } + + Crc = ~Crc; + } + 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..55af5ad7c --- /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); +} + +#define DIGEST_CBLOCK_MD2 16 +#define 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); + +static 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, +}; + +static 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; } + +static 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); +} + +static 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); + } +} + +static 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); + +static 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); + +static 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..bae0f40b3 --- /dev/null +++ b/sal/rtl/locale.cxx @@ -0,0 +1,142 @@ +/* -*- 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_pLocaleTable = nullptr; + +static rtl_Locale* g_pDefaultLocale = nullptr; + +void rtl_locale_init() +{ + if (!g_pLocaleTable) + g_pLocaleTable = new std::unordered_map<sal_Int32, locale_unique_ptr>; +} + +void rtl_locale_fini() +{ + if (g_pLocaleTable) + { + delete g_pLocaleTable; + g_pLocaleTable = nullptr; + } + 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_pLocaleTable->find(hashCode); + if (it != g_pLocaleTable->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_pLocaleTable->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..bd9d2c8a6 --- /dev/null +++ b/sal/rtl/math.cxx @@ -0,0 +1,1410 @@ +/* -*- 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/alloc.h> +#include <rtl/character.hxx> +#include <rtl/math.hxx> +#include <rtl/strbuf.h> +#include <rtl/string.h> +#include <rtl/ustrbuf.h> +#include <rtl/ustring.h> +#include <sal/mathconf.h> +#include <sal/types.h> + +#include <algorithm> +#include <cassert> +#include <float.h> +#include <limits> +#include <limits.h> +#include <math.h> +#include <memory> +#include <stdlib.h> + +#include <dtoa.h> + +static int const n10Count = 16; +static double const n10s[2][n10Count] = { + { 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, + 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16 }, + { 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, + 1e-9, 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16 } +}; + +// return pow(10.0,nExp) optimized for exponents in the interval [-16,16] +static double getN10Exp(int nExp) +{ + if (nExp < 0) + { + // && -nExp > 0 necessary for std::numeric_limits<int>::min() + // because -nExp = nExp + if (-nExp <= n10Count && -nExp > 0) + return n10s[1][-nExp-1]; + return pow(10.0, static_cast<double>(nExp)); + } + if (nExp > 0) + { + if (nExp <= n10Count) + return n10s[0][nExp-1]; + + return pow(10.0, static_cast<double>(nExp)); + } + return 1.0; +} + +namespace { + +double const nCorrVal[] = { + 0, 9e-1, 9e-2, 9e-3, 9e-4, 9e-5, 9e-6, 9e-7, 9e-8, + 9e-9, 9e-10, 9e-11, 9e-12, 9e-13, 9e-14, 9e-15 +}; + +struct StringTraits +{ + typedef sal_Char Char; + + typedef rtl_String String; + + static void createString(rtl_String ** pString, + char const * pChars, sal_Int32 nLen) + { + rtl_string_newFromStr_WithLength(pString, pChars, nLen); + } + + static void createBuffer(rtl_String ** pBuffer, + const sal_Int32 * pCapacity) + { + rtl_string_new_WithLength(pBuffer, *pCapacity); + } + + static void appendChars(rtl_String ** pBuffer, sal_Int32 * pCapacity, + sal_Int32 * pOffset, char const * pChars, + sal_Int32 nLen) + { + assert(pChars); + rtl_stringbuffer_insert(pBuffer, pCapacity, *pOffset, pChars, nLen); + *pOffset += nLen; + } + + static void appendAscii(rtl_String ** pBuffer, sal_Int32 * pCapacity, + sal_Int32 * pOffset, char const * pStr, + sal_Int32 nLen) + { + assert(pStr); + rtl_stringbuffer_insert(pBuffer, pCapacity, *pOffset, pStr, nLen); + *pOffset += nLen; + } +}; + +struct UStringTraits +{ + typedef sal_Unicode Char; + + typedef rtl_uString String; + + static void createString(rtl_uString ** pString, + sal_Unicode const * pChars, sal_Int32 nLen) + { + rtl_uString_newFromStr_WithLength(pString, pChars, nLen); + } + + static void createBuffer(rtl_uString ** pBuffer, + const sal_Int32 * pCapacity) + { + rtl_uString_new_WithLength(pBuffer, *pCapacity); + } + + static void appendChars(rtl_uString ** pBuffer, + sal_Int32 * pCapacity, sal_Int32 * pOffset, + sal_Unicode const * pChars, sal_Int32 nLen) + { + assert(pChars); + rtl_uStringbuffer_insert(pBuffer, pCapacity, *pOffset, pChars, nLen); + *pOffset += nLen; + } + + static void appendAscii(rtl_uString ** pBuffer, + sal_Int32 * pCapacity, sal_Int32 * pOffset, + char const * pStr, sal_Int32 nLen) + { + rtl_uStringbuffer_insert_ascii(pBuffer, pCapacity, *pOffset, pStr, + nLen); + *pOffset += nLen; + } +}; + +/** 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); +} + +template< typename T > +void doubleToString(typename T::String ** pResult, + sal_Int32 * pResultCapacity, sal_Int32 nResultOffset, + double fValue, rtl_math_StringFormat eFormat, + sal_Int32 nDecPlaces, typename T::Char cDecSeparator, + sal_Int32 const * pGroups, + typename T::Char cGroupSeparator, + bool bEraseTrailingDecZeros) +{ + static double const nRoundVal[] = { + 5.0e+0, 0.5e+0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6, + 0.5e-7, 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14 + }; + + // sign adjustment, instead of testing for fValue<0.0 this will also fetch + // -0.0 + bool bSign = std::signbit(fValue); + + if (bSign) + fValue = -fValue; + + if (std::isnan(fValue)) + { + // #i112652# XMLSchema-2 + sal_Int32 nCapacity = RTL_CONSTASCII_LENGTH("NaN"); + if (!pResultCapacity) + { + pResultCapacity = &nCapacity; + T::createBuffer(pResult, pResultCapacity); + nResultOffset = 0; + } + + T::appendAscii(pResult, pResultCapacity, &nResultOffset, + RTL_CONSTASCII_STRINGPARAM("NaN")); + + return; + } + + bool bHuge = fValue == HUGE_VAL; // g++ 3.0.1 requires it this way... + if (bHuge || std::isinf(fValue)) + { + // #i112652# XMLSchema-2 + sal_Int32 nCapacity = RTL_CONSTASCII_LENGTH("-INF"); + if (!pResultCapacity) + { + pResultCapacity = &nCapacity; + T::createBuffer(pResult, pResultCapacity); + nResultOffset = 0; + } + + if ( bSign ) + T::appendAscii(pResult, pResultCapacity, &nResultOffset, + RTL_CONSTASCII_STRINGPARAM("-")); + + T::appendAscii(pResult, pResultCapacity, &nResultOffset, + RTL_CONSTASCII_STRINGPARAM("INF")); + + return; + } + + // Unfortunately the old rounding below writes 1.79769313486232e+308 for + // DBL_MAX and 4 subsequent nextafter(...,0). + static const double fB1 = std::nextafter( DBL_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. + + // Writing pDig up to decimals(-1,-2) then appending one digit from + // pRou xor one or two digits from pSlot[]. + constexpr char pDig[] = "7976931348623157"; + constexpr char pRou[] = "8087931359623267"; // the only up-carry is 80 + static_assert(SAL_N_ELEMENTS(pDig) == SAL_N_ELEMENTS(pRou), "digit count mismatch"); + constexpr sal_Int32 nDig2 = RTL_CONSTASCII_LENGTH(pRou) - 2; + sal_Int32 nCapacity = RTL_CONSTASCII_LENGTH(pRou) + 8; // + "-1.E+308" + const char pSlot[5][2][3] = + { // rounded, not + "67", "57", // DBL_MAX + "65", "55", + "53", "53", + "51", "51", + "59", "49", + }; + + if (!pResultCapacity) + { + pResultCapacity = &nCapacity; + T::createBuffer(pResult, pResultCapacity); + nResultOffset = 0; + } + + if (bSign) + T::appendAscii(pResult, pResultCapacity, &nResultOffset, + RTL_CONSTASCII_STRINGPARAM("-")); + + nDecPlaces = std::clamp<sal_Int32>( nDecPlaces, 0, RTL_CONSTASCII_LENGTH(pRou)); + if (nDecPlaces == 0) + { + T::appendAscii(pResult, pResultCapacity, &nResultOffset, + RTL_CONSTASCII_STRINGPARAM("2")); + } + else + { + T::appendAscii(pResult, pResultCapacity, &nResultOffset, + RTL_CONSTASCII_STRINGPARAM("1")); + T::appendChars(pResult, pResultCapacity, &nResultOffset, &cDecSeparator, 1); + if (nDecPlaces <= 2) + { + T::appendAscii(pResult, pResultCapacity, &nResultOffset, pRou, nDecPlaces); + } + else if (nDecPlaces <= nDig2) + { + T::appendAscii(pResult, pResultCapacity, &nResultOffset, pDig, nDecPlaces - 1); + T::appendAscii(pResult, pResultCapacity, &nResultOffset, pRou + nDecPlaces - 1, 1); + } + else + { + const sal_Int32 nDec = nDecPlaces - nDig2; + nDecPlaces -= nDec; + // nDec-1 is also offset into slot, rounded(1-1=0) or not(2-1=1) + const size_t nSlot = ((fValue < fB3) ? 4 : ((fValue < fB2) ? 3 + : ((fValue < fB1) ? 2 : ((fValue < DBL_MAX) ? 1 : 0)))); + + T::appendAscii(pResult, pResultCapacity, &nResultOffset, pDig, nDecPlaces); + T::appendAscii(pResult, pResultCapacity, &nResultOffset, pSlot[nSlot][nDec-1], nDec); + } + } + T::appendAscii(pResult, pResultCapacity, &nResultOffset, + RTL_CONSTASCII_STRINGPARAM("E+308")); + + return; + } + + // Use integer representation for integer values that fit into the + // mantissa (1.((2^53)-1)) with a precision of 1 for highest accuracy. + const sal_Int64 kMaxInt = (static_cast< sal_Int64 >(1) << 53) - 1; + if ((eFormat == rtl_math_StringFormat_Automatic || + eFormat == rtl_math_StringFormat_F) && fValue <= static_cast< double >(kMaxInt)) + { + sal_Int64 nInt = static_cast< sal_Int64 >(fValue); + // Check the integer range again because double comparison may yield + // true within the precision range. + if (nInt <= kMaxInt && static_cast< double >(nInt) == fValue) + { + if (nDecPlaces == rtl_math_DecimalPlaces_Max) + nDecPlaces = 0; + else + nDecPlaces = ::std::max< sal_Int32 >(::std::min<sal_Int32>(nDecPlaces, 15), -15); + + if (bEraseTrailingDecZeros && nDecPlaces > 0) + nDecPlaces = 0; + + // Round before decimal position. + if (nDecPlaces < 0) + { + sal_Int64 nRounding = static_cast< sal_Int64 >(getN10Exp(-nDecPlaces - 1)); + sal_Int64 nTemp = nInt / nRounding; + int nDigit = nTemp % 10; + nTemp /= 10; + + if (nDigit >= 5) + ++nTemp; + + nTemp *= 10; + nTemp *= nRounding; + nInt = nTemp; + nDecPlaces = 0; + } + + // Max 1 sign, 16 integer digits, 15 group separators, 1 decimal + // separator, 15 decimals digits. + typename T::Char aBuf[64]; + typename T::Char * pBuf = aBuf; + typename T::Char * p = pBuf; + + // Backward fill. + size_t nGrouping = 0; + sal_Int32 nGroupDigits = 0; + do + { + typename T::Char nDigit = nInt % 10; + nInt /= 10; + *p++ = nDigit + '0'; + if (pGroups && pGroups[nGrouping] == ++nGroupDigits && nInt > 0 && cGroupSeparator) + { + *p++ = cGroupSeparator; + if (pGroups[nGrouping+1]) + ++nGrouping; + nGroupDigits = 0; + } + } + while (nInt > 0); + if (bSign) + *p++ = '-'; + + // Reverse buffer content. + sal_Int32 n = (p - pBuf) / 2; + for (sal_Int32 i=0; i < n; ++i) + { + ::std::swap( pBuf[i], p[-i-1]); + } + + // Append decimals. + if (nDecPlaces > 0) + { + *p++ = cDecSeparator; + while (nDecPlaces--) + *p++ = '0'; + } + + if (!pResultCapacity) + T::createString(pResult, pBuf, p - pBuf); + else + T::appendChars(pResult, pResultCapacity, &nResultOffset, pBuf, p - pBuf); + + return; + } + } + + // find the exponent + int nExp = 0; + if ( fValue > 0.0 ) + { + // Cap nExp at a small value beyond which "fValue /= N10Exp" would lose precision (or N10Exp + // might even be zero); that will produce output with the decimal point in a non-normalized + // position, but the current quality of output for such small values is probably abysmal, + // anyway: + nExp = std::max( + static_cast< int >(floor(log10(fValue))), std::numeric_limits<double>::min_exponent10); + double const N10Exp = getN10Exp(nExp); + assert(N10Exp != 0); + fValue /= N10Exp; + } + + switch (eFormat) + { + case rtl_math_StringFormat_Automatic: + { // E or F depending on exponent magnitude + int nPrec; + if (nExp <= -15 || nExp >= 15) // was <-16, >16 in ancient versions, which leads to inaccuracies + { + nPrec = 14; + eFormat = rtl_math_StringFormat_E; + } + else + { + if (nExp < 14) + { + nPrec = 15 - nExp - 1; + eFormat = rtl_math_StringFormat_F; + } + else + { + nPrec = 15; + eFormat = rtl_math_StringFormat_F; + } + } + + if (nDecPlaces == rtl_math_DecimalPlaces_Max) + nDecPlaces = nPrec; + } + 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::max<sal_Int32>(std::min<sal_Int32>(nDecPlaces, 20), -20); + + sal_Int32 nDigits = nDecPlaces + 1; + + if (eFormat == rtl_math_StringFormat_F) + nDigits += nExp; + + // Round the number + if(nDigits >= 0) + { + fValue += nRoundVal[std::min<sal_Int32>(nDigits, 15)]; + if (fValue >= 10) + { + fValue = 1.0; + nExp++; + + if (eFormat == rtl_math_StringFormat_F) + nDigits++; + } + } + + static sal_Int32 const nBufMax = 256; + typename T::Char aBuf[nBufMax]; + typename T::Char * pBuf; + sal_Int32 nBuf = static_cast< sal_Int32 > + (nDigits <= 0 ? std::max< sal_Int32 >(nDecPlaces, abs(nExp)) + : nDigits + nDecPlaces ) + 10 + (pGroups ? abs(nDigits) * 2 : 0); + + if (nBuf > nBufMax) + { + pBuf = static_cast< typename T::Char * >( + malloc(nBuf * sizeof (typename T::Char))); + OSL_ENSURE(pBuf, "Out of memory"); + } + else + { + pBuf = aBuf; + } + + typename T::Char * p = pBuf; + if ( bSign ) + *p++ = static_cast< typename T::Char >('-'); + + bool bHasDec = false; + + int nDecPos; + // Check for F format and number < 1 + if(eFormat == rtl_math_StringFormat_F) + { + if(nExp < 0) + { + *p++ = static_cast< typename T::Char >('0'); + if (nDecPlaces > 0) + { + *p++ = cDecSeparator; + bHasDec = true; + } + + sal_Int32 i = (nDigits <= 0 ? nDecPlaces : -nExp - 1); + + while((i--) > 0) + { + *p++ = static_cast< typename T::Char >('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 i = 0; ; i++) + { + if (i < 15) // was 16 in ancient versions, which leads to inaccuracies + { + int nDigit; + if (nDigits-1 == 0 && i > 0 && i < 14) + nDigit = static_cast< int >(floor( fValue + nCorrVal[15-i])); + else + nDigit = static_cast< int >(fValue + 1E-15); + + if (nDigit >= 10) + { // after-treatment of up-rounding to the next decade + sal_Int32 sLen = static_cast< long >(p-pBuf)-1; + if (sLen == -1 || (sLen == 0 && bSign)) + { + // Assert that no one changed the logic we rely on. + assert(!bSign || *pBuf == static_cast< typename T::Char >('-')); + p = pBuf; + if (bSign) + ++p; + if (eFormat == rtl_math_StringFormat_F) + { + *p++ = static_cast< typename T::Char >('1'); + *p++ = static_cast< typename T::Char >('0'); + } + else + { + *p++ = static_cast< typename T::Char >('1'); + *p++ = cDecSeparator; + *p++ = static_cast< typename T::Char >('0'); + nExp++; + bHasDec = true; + } + } + else + { + for (sal_Int32 j = sLen; j >= 0; j--) + { + typename T::Char cS = pBuf[j]; + if (j == 0 && bSign) + { + // Do not touch leading minus sign put earlier. + assert(cS == static_cast< typename T::Char >('-')); + break; // for, this is the last character backwards. + } + if (cS != cDecSeparator) + { + if (cS != static_cast< typename T::Char >('9')) + { + pBuf[j] = ++cS; + j = -1; // break loop + } + else + { + pBuf[j] = static_cast< typename T::Char >('0'); + if (j == 0 || (j == 1 && bSign)) + { + if (eFormat == rtl_math_StringFormat_F) + { // insert '1' + typename T::Char * px = p++; + while (pBuf < px) + { + *px = *(px-1); + px--; + } + + pBuf[0] = static_cast< typename T::Char >('1'); + } + else + { + pBuf[j] = static_cast< typename T::Char >('1'); + nExp++; + } + } + } + } + } + + *p++ = static_cast< typename T::Char >('0'); + } + fValue = 0.0; + } + else + { + *p++ = static_cast< typename T::Char >( + nDigit + static_cast< typename T::Char >('0') ); + fValue = (fValue - nDigit) * 10.0; + } + } + else + { + *p++ = static_cast< typename T::Char >('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++ = static_cast< typename T::Char >('0'); + } + } + + if (bEraseTrailingDecZeros && bHasDec && p > pBuf) + { + while (*(p-1) == static_cast< typename T::Char >('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++ = static_cast< typename T::Char >('1'); + // maybe no nDigits if nDecPlaces < 0 + + *p++ = static_cast< typename T::Char >('E'); + if(nExp < 0) + { + nExp = -nExp; + *p++ = static_cast< typename T::Char >('-'); + } + else + { + *p++ = static_cast< typename T::Char >('+'); + } + + if (eFormat == rtl_math_StringFormat_E || nExp >= 100) + *p++ = static_cast< typename T::Char >( + nExp / 100 + static_cast< typename T::Char >('0') ); + + nExp %= 100; + + if (eFormat == rtl_math_StringFormat_E || eFormat == rtl_math_StringFormat_E2 || nExp >= 10) + *p++ = static_cast< typename T::Char >( + nExp / 10 + static_cast< typename T::Char >('0') ); + + *p++ = static_cast< typename T::Char >( + nExp % 10 + static_cast< typename T::Char >('0') ); + } + + if (!pResultCapacity) + T::createString(pResult, pBuf, p - pBuf); + else + T::appendChars(pResult, pResultCapacity, &nResultOffset, pBuf, p - pBuf); + + if (pBuf != &aBuf[0]) + free(pBuf); +} + +} + +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() +{ + doubleToString< StringTraits >( + 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() +{ + doubleToString< UStringTraits >( + 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; + if (p0 != pEnd && *p0 == CharT('-')) + { + bSign = true; + ++p0; + } + else + { + bSign = false; + if (p0 != pEnd && *p0 == CharT('+')) + ++p0; + } + + CharT const * p = p0; + bool bDone = false; + + // #i112652# XMLSchema-2 + if ((pEnd - p) >= 3) + { + if ((CharT('N') == p[0]) && (CharT('a') == p[1]) + && (CharT('N') == p[2])) + { + p += 3; + rtl::math::setNan( &fVal ); + 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; + rtl::math::setNan( &fVal ); + if (bSign) + { + union { + double sd; + sal_math_Double md; + } m; + + m.sd = fVal; + m.md.w32_parts.msw |= 0x80000000; // create negative NaN + fVal = m.sd; + 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() +{ + OSL_ASSERT(nDecPlaces >= -20 && nDecPlaces <= 20); + + if (fValue == 0.0) + return fValue; + + if ( nDecPlaces == 0 && eMode == rtl_math_RoundingMode_Corrected ) + return std::round( fValue ); + + // sign adjustment + bool bSign = std::signbit( fValue ); + if (bSign) + fValue = -fValue; + + double fFac = 0; + if (nDecPlaces != 0) + { + // max 20 decimals, we don't have unlimited precision + // #38810# and no overflow on fValue*=fFac + if (nDecPlaces < -20 || 20 < nDecPlaces || fValue > (DBL_MAX / 1e20)) + return bSign ? -fValue : fValue; + + fFac = getN10Exp(nDecPlaces); + fValue *= fFac; + } + + switch ( eMode ) + { + case rtl_math_RoundingMode_Corrected : + { + int nExp; // exponent for correction + if ( fValue > 0.0 ) + nExp = static_cast<int>( floor( log10( fValue ) ) ); + else + nExp = 0; + + int nIndex; + + if (nExp < 0) + nIndex = 15; + else if (nExp >= 14) + nIndex = 0; + else + nIndex = 15 - nExp; + + fValue = floor(fValue + 0.5 + nCorrVal[nIndex]); + } + 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) + fValue /= fFac; + + 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 = 2199023255552.0; // 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(nExp); + + 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 = rtl_math_round(fValue, 0, rtl_math_RoundingMode_Corrected); + 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 = 1.0 / (16777216.0 * 16777216.0); + static const double e44 = e48 * 16.0; + + 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() +#if defined __clang__ + __attribute__((no_sanitize("float-divide-by-zero"))) // atahn(1) -> inf +#endif +{ + return 0.5 * rtl_math_log1p(2.0 * fValue / (1.0-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) + { + double fResult; + ::rtl::math::setNan( &fResult ); + return fResult; + } + 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..42c24e800 --- /dev/null +++ b/sal/rtl/rtl_process.cxx @@ -0,0 +1,58 @@ +/* -*- 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/instance.hxx> +#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]; +}; + +struct theId: public rtl::Static< Id, theId > {}; + +} // end namespace + +void rtl_getGlobalProcessId(sal_uInt8 * pTargetUUID) +{ + theId::get().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..b9f4cd688 --- /dev/null +++ b/sal/rtl/strbuf.cxx @@ -0,0 +1,182 @@ +/* -*- 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" + +/************************************************************************* + * rtl_stringbuffer_newFromStr_WithLength + */ +void SAL_CALL rtl_stringbuffer_newFromStr_WithLength( rtl_String ** newStr, + const char * value, + sal_Int32 count ) +{ + assert(newStr); + assert(count >= 0); + if (!value) + { + rtl_string_new_WithLength( newStr, 16 ); + return; + } + + // use raw alloc to avoid overwriting the buffer twice + if ( *newStr) + rtl_string_release( *newStr ); + *newStr = rtl_string_ImplAlloc( count + 16 ); + (*newStr)->length = count; + memcpy( (*newStr)->buffer, value, count ); + memset( (*newStr)->buffer + count, 0, 16 ); +} + +/************************************************************************* + * rtl_stringbuffer_newFromStringBuffer + */ +sal_Int32 SAL_CALL rtl_stringbuffer_newFromStringBuffer( rtl_String ** newStr, + sal_Int32 capacity, + rtl_String * oldStr ) +{ + assert(newStr); + assert(oldStr); + assert(capacity >= 0); + sal_Int32 newCapacity = capacity; + + if (newCapacity < oldStr->length) + newCapacity = oldStr->length; + + rtl_string_new_WithLength( newStr, newCapacity ); + if (oldStr->length > 0) { + (*newStr)->length = oldStr->length; + memcpy( (*newStr)->buffer, oldStr->buffer, oldStr->length ); + } + return newCapacity; +} + +/************************************************************************* + * rtl_stringbuffer_ensureCapacity + */ +void SAL_CALL rtl_stringbuffer_ensureCapacity + (rtl_String ** This, sal_Int32* capacity, sal_Int32 minimumCapacity) +{ + assert(This); +// assert(capacity && *capacity >= 0); +// assert(minimumCapacity >= 0); + if (minimumCapacity <= *capacity) + return; + + rtl_String * pTmp = *This; + rtl_String * pNew = nullptr; + auto nLength = (*This)->length; + *capacity = (nLength + 1) * 2; + if (minimumCapacity > *capacity) + /* still lower, set to the minimum capacity */ + *capacity = minimumCapacity; + + // use raw alloc to avoid overwriting the buffer twice + pNew = rtl_string_ImplAlloc( *capacity ); + pNew->length = nLength; + *This = pNew; + + memcpy( (*This)->buffer, pTmp->buffer, nLength ); + memset( (*This)->buffer + nLength, 0, *capacity - nLength ); + rtl_string_release( pTmp ); +} + +/************************************************************************* + * rtl_stringbuffer_insert + */ +void SAL_CALL rtl_stringbuffer_insert( rtl_String ** This, + sal_Int32 * capacity, + sal_Int32 offset, + const char * str, + sal_Int32 len ) +{ + assert(This); + assert(capacity && *capacity >= 0); + assert(offset >= 0 && offset <= (**This).length); + assert(len >= 0); + sal_Int32 nOldLen; + char * pBuf; + sal_Int32 n; + if( len == 0 ) + return; + + if (*capacity < (*This)->length + len) + rtl_stringbuffer_ensureCapacity( This, capacity, (*This)->length + len ); + + nOldLen = (*This)->length; + pBuf = (*This)->buffer; + + /* copy the tail */ + n = (nOldLen - offset); + if( n == 1 ) + /* optimized for 1 character */ + pBuf[offset + len] = pBuf[offset]; + else if( n > 1 ) + memmove( pBuf + offset + len, pBuf + offset, n * sizeof(char) ); + + /* insert the new characters */ + if( str != nullptr ) + { + if( len == 1 ) + /* optimized for 1 character */ + pBuf[offset] = *str; + else + memcpy( pBuf + offset, str, len * sizeof(char) ); + } + (*This)->length = nOldLen + len; + pBuf[ nOldLen + len ] = 0; +} + +/************************************************************************* + * rtl_stringbuffer_remove + */ +void SAL_CALL rtl_stringbuffer_remove( rtl_String ** This, + sal_Int32 start, + sal_Int32 len ) +{ + assert(This); + assert(start >= 0 && start <= (**This).length); + assert(len >= 0); + sal_Int32 nTailLen; + char * pBuf; + + if (len > (*This)->length - start) + len = (*This)->length - start; + + //remove nothing + if (!len) + return; + + pBuf = (*This)->buffer; + nTailLen = (*This)->length - ( start + len ); + + if (nTailLen) + { + /* move the tail */ + memmove(pBuf + start, pBuf + start + len, nTailLen * sizeof(char)); + } + + (*This)->length-=len; + pBuf[ (*This)->length ] = 0; +} + +/* 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..51a1a94bd --- /dev/null +++ b/sal/rtl/strimp.cxx @@ -0,0 +1,120 @@ +/* -*- 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" + +sal_Int16 rtl_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; +} + +bool rtl_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; +} + +/* + * 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..f3516f799 --- /dev/null +++ b/sal/rtl/strimp.hxx @@ -0,0 +1,96 @@ +/* -*- 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) + +sal_Int16 rtl_ImplGetDigit( sal_Unicode ch, sal_Int16 nRadix ); + +bool rtl_ImplIsWhitespace( sal_Unicode c ); + +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..13bf7542c --- /dev/null +++ b/sal/rtl/string.cxx @@ -0,0 +1,394 @@ +/* -*- 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 <rtl/alloc.h> +#include <osl/diagnose.h> +#include <rtl/tencinfo.h> + +#include "strimp.hxx" +#include <rtl/character.hxx> +#include <rtl/string.h> + +#include <rtl/math.h> + +/* ======================================================================= */ + +/* static data to be referenced by all empty strings + * the refCount is predefined to 1 and must never become 0 ! + */ +static rtl_String const aImplEmpty_rtl_String = +{ + SAL_STRING_STATIC_FLAG|1, + /* sal_Int32 refCount; */ + 0, /* sal_Int32 length; */ + { 0 } /* char buffer[1]; */ +}; + +/* ======================================================================= */ +/* These macros are for the "poor-man templates" included from + * the strtmpl.cxx just below, used to share code between here and + * ustring.cxx + */ + +#define IMPL_RTL_IS_USTRING 0 + +#define IMPL_RTL_STRCODE char +#define IMPL_RTL_USTRCODE( c ) (static_cast<unsigned char>(c)) +#define IMPL_RTL_STRNAME( n ) rtl_str_ ## n + +#define IMPL_RTL_STRINGNAME( n ) rtl_string_ ## n +#define IMPL_RTL_STRINGDATA rtl_String +#define IMPL_RTL_EMPTYSTRING aImplEmpty_rtl_String + +#if USE_SDT_PROBES +#define RTL_LOG_STRING_BITS 8 +#endif + +/* ======================================================================= */ + +/* Include String/UString template code */ + +#include "strtmpl.cxx" + +#undef IMPL_RTL_EMPTYSTRING +#undef IMPL_RTL_IS_USTRING +#undef IMPL_RTL_STRCODE +#undef IMPL_RTL_STRINGDATA +#undef IMPL_RTL_STRINGNAME +#undef IMPL_RTL_STRNAME +#undef IMPL_RTL_USTRCODE +#undef RTL_LOG_STRING_BITS + +sal_Int32 SAL_CALL rtl_str_valueOfFloat(char * pStr, float f) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + rtl_String * pResult = nullptr; + sal_Int32 nLen; + rtl_math_doubleToString( + &pResult, nullptr, 0, f, rtl_math_StringFormat_G, + RTL_STR_MAX_VALUEOFFLOAT - RTL_CONSTASCII_LENGTH("-x.E-xxx"), '.', nullptr, 0, + true); + nLen = pResult->length; + OSL_ASSERT(nLen < RTL_STR_MAX_VALUEOFFLOAT); + memcpy(pStr, pResult->buffer, (nLen + 1) * sizeof(char)); + rtl_string_release(pResult); + return nLen; +} + +sal_Int32 SAL_CALL rtl_str_valueOfDouble(char * pStr, double d) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + rtl_String * pResult = nullptr; + sal_Int32 nLen; + rtl_math_doubleToString( + &pResult, nullptr, 0, d, rtl_math_StringFormat_G, + RTL_STR_MAX_VALUEOFDOUBLE - RTL_CONSTASCII_LENGTH("-x.E-xxx"), '.', nullptr, + 0, true); + nLen = pResult->length; + OSL_ASSERT(nLen < RTL_STR_MAX_VALUEOFDOUBLE); + memcpy(pStr, pResult->buffer, (nLen + 1) * sizeof(char)); + rtl_string_release(pResult); + return nLen; +} + +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_ImplCopy( 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(); + } + sal_Int32 n = str->length - fromLength + toLength; + rtl_string_acquire(str); // in case *newStr == str + rtl_string_new_WithLength(newStr, n); + if (n != 0) { + (*newStr)->length = n; + assert(i >= 0 && i < str->length); + memcpy((*newStr)->buffer, str->buffer, i); + memcpy((*newStr)->buffer + i, to, toLength); + memcpy( + (*newStr)->buffer + i + toLength, str->buffer + i + fromLength, + str->length - i - fromLength); + } + rtl_string_release(str); + } + *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_string_assign(newStr, str); + for (sal_Int32 i = 0;; i += toLength) { + rtl_string_newReplaceFirst( + newStr, *newStr, from, fromLength, to, toLength, &i); + if (i == -1) { + break; + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/strtmpl.cxx b/sal/rtl/strtmpl.cxx new file mode 100644 index 000000000..8ff170767 --- /dev/null +++ b/sal/rtl/strtmpl.cxx @@ -0,0 +1,1911 @@ +/* -*- 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 . + */ + +/* ======================================================================= */ +/* Internal C-String help functions which could be used without the */ +/* String-Class */ +/* ======================================================================= */ + +#include <algorithm> +#include <cassert> +#include <limits> + +#include <cstring> +#include <wchar.h> +#include <sal/log.hxx> +#include <rtl/character.hxx> + +/* +inline void rtl_str_ImplCopy( IMPL_RTL_STRCODE* pDest, + const IMPL_RTL_STRCODE* pSrc, + sal_Int32 nCount ) +{ + while ( nCount > 0 ) + { + *pDest = *pSrc; + pDest++; + pSrc++; + nCount--; + } +} +*/ + +static void rtl_str_ImplCopy( IMPL_RTL_STRCODE* _pDest, + const IMPL_RTL_STRCODE* _pSrc, + sal_Int32 _nCount ) +{ + // take advantage of builtin optimisations + memcpy( _pDest, _pSrc, _nCount * sizeof(IMPL_RTL_STRCODE)); +} + +/* ======================================================================= */ +/* C-String functions which could be used without the String-Class */ +/* ======================================================================= */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( getLength )( const IMPL_RTL_STRCODE* pStr ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); +#if !IMPL_RTL_IS_USTRING + // take advantage of builtin optimisations + return strlen( pStr); +#else + if (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t)) + { + // take advantage of builtin optimisations + return wcslen(reinterpret_cast<wchar_t const *>(pStr)); + } + else + { + const IMPL_RTL_STRCODE* pTempStr = pStr; + while( *pTempStr ) + pTempStr++; + return pTempStr-pStr; + } +#endif +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( compare )( const IMPL_RTL_STRCODE* pStr1, + const IMPL_RTL_STRCODE* pStr2 ) + SAL_THROW_EXTERN_C() +{ + assert(pStr1); + assert(pStr2); +#if !IMPL_RTL_IS_USTRING + // take advantage of builtin optimisations + return strcmp( pStr1, pStr2); +#else + if (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t)) + { + // take advantage of builtin optimisations + return wcscmp(reinterpret_cast<wchar_t const *>(pStr1), reinterpret_cast<wchar_t const *>(pStr2)); + } + else + { + sal_Int32 nRet; + for (;;) + { + nRet = static_cast<sal_Int32>(IMPL_RTL_USTRCODE(*pStr1)) - + static_cast<sal_Int32>(IMPL_RTL_USTRCODE(*pStr2)); + if (!(nRet == 0 && *pStr2 )) + break; + pStr1++; + pStr2++; + } + + return nRet; + } +#endif +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( compare_WithLength )( const IMPL_RTL_STRCODE* pStr1, + sal_Int32 nStr1Len, + const IMPL_RTL_STRCODE* pStr2, + sal_Int32 nStr2Len ) + SAL_THROW_EXTERN_C() +{ + assert(nStr1Len >= 0); + assert(nStr2Len >= 0); +#if !IMPL_RTL_IS_USTRING + // take advantage of builtin optimisations + sal_Int32 nMin = std::min(nStr1Len, nStr2Len); + sal_Int32 nRet = memcmp(pStr1, pStr2, nMin); + return nRet == 0 ? nStr1Len - nStr2Len : nRet; +#else + if (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t)) + { + // take advantage of builtin optimisations + sal_Int32 nMin = std::min(nStr1Len, nStr2Len); + sal_Int32 nRet = wmemcmp(reinterpret_cast<wchar_t const *>(pStr1), + reinterpret_cast<wchar_t const *>(pStr2), nMin); + return nRet == 0 ? nStr1Len - nStr2Len : nRet; + } + else + { + sal_Int32 nRet = nStr1Len - nStr2Len; + int nCount = (nRet <= 0) ? nStr1Len : nStr2Len; + + --pStr1; + --pStr2; + while( (--nCount >= 0) && (*++pStr1 == *++pStr2) ) ; + + if( nCount >= 0 ) + nRet = static_cast<sal_Int32>(IMPL_RTL_USTRCODE( *pStr1 )) + - static_cast<sal_Int32>(IMPL_RTL_USTRCODE( *pStr2 )); + + return nRet; + } +#endif +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( shortenedCompare_WithLength )( const IMPL_RTL_STRCODE* pStr1, + sal_Int32 nStr1Len, + const IMPL_RTL_STRCODE* pStr2, + sal_Int32 nStr2Len, + sal_Int32 nShortenedLength ) + SAL_THROW_EXTERN_C() +{ + assert(nStr1Len >= 0); + assert(nStr2Len >= 0); + assert(nShortenedLength >= 0); +#if !IMPL_RTL_IS_USTRING + // take advantage of builtin optimisations + sal_Int32 nMin = std::min(std::min(nStr1Len, nStr2Len), nShortenedLength); + sal_Int32 nRet = memcmp(pStr1, pStr2, nMin); + if (nRet == 0 && nShortenedLength > std::min(nStr1Len, nStr2Len)) + return nStr1Len - nStr2Len; + return nRet; +#else + if (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t)) + { + // take advantage of builtin optimisations + sal_Int32 nMin = std::min(std::min(nStr1Len, nStr2Len), nShortenedLength); + sal_Int32 nRet = wmemcmp(reinterpret_cast<wchar_t const *>(pStr1), reinterpret_cast<wchar_t const *>(pStr2), nMin); + if (nRet == 0 && nShortenedLength > std::min(nStr1Len, nStr2Len)) + return nStr1Len - nStr2Len; + return nRet; + } + else + { + const IMPL_RTL_STRCODE* pStr1End = pStr1 + nStr1Len; + const IMPL_RTL_STRCODE* pStr2End = pStr2 + nStr2Len; + sal_Int32 nRet; + while ( (nShortenedLength > 0) && + (pStr1 < pStr1End) && (pStr2 < pStr2End) ) + { + nRet = static_cast<sal_Int32>(IMPL_RTL_USTRCODE( *pStr1 ))- + static_cast<sal_Int32>(IMPL_RTL_USTRCODE( *pStr2 )); + if ( nRet ) + return nRet; + + nShortenedLength--; + pStr1++; + pStr2++; + } + + if ( nShortenedLength <= 0 ) + return 0; + return nStr1Len - nStr2Len; + } +#endif +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( reverseCompare_WithLength )( const IMPL_RTL_STRCODE* pStr1, + sal_Int32 nStr1Len, + const IMPL_RTL_STRCODE* pStr2, + sal_Int32 nStr2Len ) + SAL_THROW_EXTERN_C() +{ + assert(nStr1Len >= 0); + assert(nStr2Len >= 0); + const IMPL_RTL_STRCODE* pStr1Run = pStr1+nStr1Len; + const IMPL_RTL_STRCODE* pStr2Run = pStr2+nStr2Len; + sal_Int32 nRet; + while ( (pStr1 < pStr1Run) && (pStr2 < pStr2Run) ) + { + pStr1Run--; + pStr2Run--; + nRet = static_cast<sal_Int32>(IMPL_RTL_USTRCODE( *pStr1Run ))- + static_cast<sal_Int32>(IMPL_RTL_USTRCODE( *pStr2Run )); + if ( nRet ) + return nRet; + } + + return nStr1Len - nStr2Len; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( compareIgnoreAsciiCase )( const IMPL_RTL_STRCODE* pStr1, + const IMPL_RTL_STRCODE* pStr2 ) + SAL_THROW_EXTERN_C() +{ + assert(pStr1); + assert(pStr2); + sal_uInt32 c1; + do + { + c1 = IMPL_RTL_USTRCODE(*pStr1); + sal_Int32 nRet = rtl::compareIgnoreAsciiCase( + c1, IMPL_RTL_USTRCODE(*pStr2)); + if ( nRet != 0 ) + return nRet; + + pStr1++; + pStr2++; + } + while (c1); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( compareIgnoreAsciiCase_WithLength )( const IMPL_RTL_STRCODE* pStr1, + sal_Int32 nStr1Len, + const IMPL_RTL_STRCODE* pStr2, + sal_Int32 nStr2Len ) + SAL_THROW_EXTERN_C() +{ + assert(nStr1Len >= 0); + assert(nStr2Len >= 0); + const IMPL_RTL_STRCODE* pStr1End = pStr1 + nStr1Len; + const IMPL_RTL_STRCODE* pStr2End = pStr2 + nStr2Len; + while ( (pStr1 < pStr1End) && (pStr2 < pStr2End) ) + { + sal_Int32 nRet = rtl::compareIgnoreAsciiCase( + IMPL_RTL_USTRCODE(*pStr1), IMPL_RTL_USTRCODE(*pStr2)); + if ( nRet != 0 ) + return nRet; + + pStr1++; + pStr2++; + } + + return nStr1Len - nStr2Len; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( shortenedCompareIgnoreAsciiCase_WithLength )( const IMPL_RTL_STRCODE* pStr1, + sal_Int32 nStr1Len, + const IMPL_RTL_STRCODE* pStr2, + sal_Int32 nStr2Len, + sal_Int32 nShortenedLength ) + SAL_THROW_EXTERN_C() +{ + assert(nStr1Len >= 0); + assert(nStr2Len >= 0); + assert(nShortenedLength >= 0); + const IMPL_RTL_STRCODE* pStr1End = pStr1 + nStr1Len; + const IMPL_RTL_STRCODE* pStr2End = pStr2 + nStr2Len; + while ( (nShortenedLength > 0) && + (pStr1 < pStr1End) && (pStr2 < pStr2End) ) + { + sal_Int32 nRet = rtl::compareIgnoreAsciiCase( + IMPL_RTL_USTRCODE(*pStr1), IMPL_RTL_USTRCODE(*pStr2)); + if ( nRet != 0 ) + return nRet; + + nShortenedLength--; + pStr1++; + pStr2++; + } + + if ( nShortenedLength <= 0 ) + return 0; + return nStr1Len - nStr2Len; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( hashCode )( const IMPL_RTL_STRCODE* pStr ) + SAL_THROW_EXTERN_C() +{ + return IMPL_RTL_STRNAME( hashCode_WithLength )( pStr, IMPL_RTL_STRNAME( getLength )( pStr ) ); +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( hashCode_WithLength )( const IMPL_RTL_STRCODE* pStr, + sal_Int32 nLen ) + SAL_THROW_EXTERN_C() +{ + 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); +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( indexOfChar )( const IMPL_RTL_STRCODE* pStr, + IMPL_RTL_STRCODE c ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); +#if !IMPL_RTL_IS_USTRING + // take advantage of builtin optimisations + const IMPL_RTL_STRCODE* p = strchr(pStr, c); + return p ? p - pStr : -1; +#else + if (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; + } +#endif +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( indexOfChar_WithLength )( const IMPL_RTL_STRCODE* pStr, + sal_Int32 nLen, + IMPL_RTL_STRCODE c ) + SAL_THROW_EXTERN_C() +{ +// assert(nLen >= 0); +#if !IMPL_RTL_IS_USTRING + // take advantage of builtin optimisations + IMPL_RTL_STRCODE* p = static_cast<IMPL_RTL_STRCODE*>(std::memchr(const_cast<IMPL_RTL_STRCODE *>(pStr), c, nLen)); + return p ? p - pStr : -1; +#else + const IMPL_RTL_STRCODE* pTempStr = pStr; + while ( nLen > 0 ) + { + if ( *pTempStr == c ) + return pTempStr-pStr; + + pTempStr++; + nLen--; + } + + return -1; +#endif +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( lastIndexOfChar )( const IMPL_RTL_STRCODE* pStr, + IMPL_RTL_STRCODE c ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); +#if !IMPL_RTL_IS_USTRING + // take advantage of builtin optimisations + const IMPL_RTL_STRCODE* p = strrchr(pStr, c); + return p ? p - pStr : -1; +#else + if (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 IMPL_RTL_STRNAME( lastIndexOfChar_WithLength )( pStr, IMPL_RTL_STRNAME( getLength )( pStr ), c ); + } +#endif +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( lastIndexOfChar_WithLength )( const IMPL_RTL_STRCODE* pStr, + sal_Int32 nLen, + IMPL_RTL_STRCODE c ) + SAL_THROW_EXTERN_C() +{ + assert(nLen >= 0); + pStr += nLen; + while ( nLen > 0 ) + { + nLen--; + pStr--; + + if ( *pStr == c ) + return nLen; + } + + return -1; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( indexOfStr )( const IMPL_RTL_STRCODE* pStr, + const IMPL_RTL_STRCODE* pSubStr ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + assert(pSubStr); +#if !IMPL_RTL_IS_USTRING + // take advantage of builtin optimisations + const IMPL_RTL_STRCODE* p = strstr(pStr, pSubStr); + return p ? p - pStr : -1; +#else + if (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 IMPL_RTL_STRNAME( indexOfStr_WithLength )( pStr, IMPL_RTL_STRNAME( getLength )( pStr ), + pSubStr, IMPL_RTL_STRNAME( getLength )( pSubStr ) ); + } +#endif +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( indexOfStr_WithLength )( const IMPL_RTL_STRCODE* pStr, + sal_Int32 nStrLen, + const IMPL_RTL_STRCODE* pSubStr, + sal_Int32 nSubLen ) + SAL_THROW_EXTERN_C() +{ + assert(nStrLen >= 0); + assert(nSubLen >= 0); + /* faster search for a single character */ + if ( nSubLen < 2 ) + { + /* an empty SubString is always not findable */ + if ( nSubLen == 1 ) + { + IMPL_RTL_STRCODE c = *pSubStr; + const IMPL_RTL_STRCODE* pTempStr = pStr; + while ( nStrLen > 0 ) + { + if ( *pTempStr == c ) + return pTempStr-pStr; + + pTempStr++; + nStrLen--; + } + } + } + else + { + const IMPL_RTL_STRCODE* pTempStr = pStr; + while ( nStrLen > 0 ) + { + if ( *pTempStr == *pSubStr ) + { + /* Compare SubString */ + if ( nSubLen <= nStrLen ) + { + const IMPL_RTL_STRCODE* pTempStr1 = pTempStr; + const IMPL_RTL_STRCODE* pTempStr2 = pSubStr; + sal_Int32 nTempLen = nSubLen; + while ( nTempLen ) + { + if ( *pTempStr1 != *pTempStr2 ) + break; + + pTempStr1++; + pTempStr2++; + nTempLen--; + } + + if ( !nTempLen ) + return pTempStr-pStr; + } + else + break; + } + + nStrLen--; + pTempStr++; + } + } + + return -1; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( lastIndexOfStr )( const IMPL_RTL_STRCODE* pStr, + const IMPL_RTL_STRCODE* pSubStr ) + SAL_THROW_EXTERN_C() +{ + return IMPL_RTL_STRNAME( lastIndexOfStr_WithLength )( pStr, IMPL_RTL_STRNAME( getLength )( pStr ), + pSubStr, IMPL_RTL_STRNAME( getLength )( pSubStr ) ); +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( lastIndexOfStr_WithLength )( const IMPL_RTL_STRCODE* pStr, + sal_Int32 nStrLen, + const IMPL_RTL_STRCODE* pSubStr, + sal_Int32 nSubLen ) + SAL_THROW_EXTERN_C() +{ + assert(nStrLen >= 0); + assert(nSubLen >= 0); + /* faster search for a single character */ + if ( nSubLen < 2 ) + { + /* an empty SubString is always not findable */ + if ( nSubLen == 1 ) + { + IMPL_RTL_STRCODE c = *pSubStr; + pStr += nStrLen; + while ( nStrLen > 0 ) + { + nStrLen--; + pStr--; + + if ( *pStr == c ) + return nStrLen; + } + } + } + else + { + pStr += nStrLen; + nStrLen -= nSubLen; + pStr -= nSubLen; + while ( nStrLen >= 0 ) + { + const IMPL_RTL_STRCODE* pTempStr1 = pStr; + const IMPL_RTL_STRCODE* pTempStr2 = pSubStr; + sal_Int32 nTempLen = nSubLen; + while ( nTempLen ) + { + if ( *pTempStr1 != *pTempStr2 ) + break; + + pTempStr1++; + pTempStr2++; + nTempLen--; + } + + if ( !nTempLen ) + return nStrLen; + + nStrLen--; + pStr--; + } + } + + return -1; +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRNAME( replaceChar )( IMPL_RTL_STRCODE* pStr, + IMPL_RTL_STRCODE cOld, + IMPL_RTL_STRCODE cNew ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + while ( *pStr ) + { + if ( *pStr == cOld ) + *pStr = cNew; + + pStr++; + } +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRNAME( replaceChar_WithLength )( IMPL_RTL_STRCODE* pStr, + sal_Int32 nLen, + IMPL_RTL_STRCODE cOld, + IMPL_RTL_STRCODE cNew ) + SAL_THROW_EXTERN_C() +{ + assert(nLen >= 0); + while ( nLen > 0 ) + { + if ( *pStr == cOld ) + *pStr = cNew; + + pStr++; + nLen--; + } +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRNAME( toAsciiLowerCase )( IMPL_RTL_STRCODE* pStr ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + while ( *pStr ) + { + *pStr = rtl::toAsciiLowerCase(IMPL_RTL_USTRCODE(*pStr)); + + pStr++; + } +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRNAME( toAsciiLowerCase_WithLength )( IMPL_RTL_STRCODE* pStr, + sal_Int32 nLen ) + SAL_THROW_EXTERN_C() +{ + assert(nLen >= 0); + while ( nLen > 0 ) + { + *pStr = rtl::toAsciiLowerCase(IMPL_RTL_USTRCODE(*pStr)); + + pStr++; + nLen--; + } +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRNAME( toAsciiUpperCase )( IMPL_RTL_STRCODE* pStr ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + while ( *pStr ) + { + *pStr = rtl::toAsciiUpperCase(IMPL_RTL_USTRCODE(*pStr)); + + pStr++; + } +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRNAME( toAsciiUpperCase_WithLength )( IMPL_RTL_STRCODE* pStr, + sal_Int32 nLen ) + SAL_THROW_EXTERN_C() +{ + assert(nLen >= 0); + while ( nLen > 0 ) + { + *pStr = rtl::toAsciiUpperCase(IMPL_RTL_USTRCODE(*pStr)); + + pStr++; + nLen--; + } +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( trim )( IMPL_RTL_STRCODE* pStr ) + SAL_THROW_EXTERN_C() +{ + return IMPL_RTL_STRNAME( trim_WithLength )( pStr, IMPL_RTL_STRNAME( getLength )( pStr ) ); +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( trim_WithLength )( IMPL_RTL_STRCODE* pStr, sal_Int32 nLen ) + SAL_THROW_EXTERN_C() +{ + assert(nLen >= 0); + sal_Int32 nPreSpaces = 0; + sal_Int32 nPostSpaces = 0; + sal_Int32 nIndex = nLen-1; + + while ( (nPreSpaces < nLen) && rtl_ImplIsWhitespace( IMPL_RTL_USTRCODE(*(pStr+nPreSpaces)) ) ) + nPreSpaces++; + + while ( (nIndex > nPreSpaces) && rtl_ImplIsWhitespace( IMPL_RTL_USTRCODE(*(pStr+nIndex)) ) ) + { + nPostSpaces++; + nIndex--; + } + + if ( nPostSpaces ) + { + nLen -= nPostSpaces; + *(pStr+nLen) = 0; + } + + if ( nPreSpaces ) + { + nLen -= nPreSpaces; + memmove(pStr, pStr + nPreSpaces, nLen * sizeof(IMPL_RTL_STRCODE)); + pStr += nLen; + *pStr = 0; + } + + return nLen; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( valueOfBoolean )( IMPL_RTL_STRCODE* pStr, sal_Bool b ) + SAL_THROW_EXTERN_C() +{ + 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; + } +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( valueOfChar )( IMPL_RTL_STRCODE* pStr, + IMPL_RTL_STRCODE c ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + *pStr++ = c; + *pStr = 0; + return 1; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( valueOfInt32 )( IMPL_RTL_STRCODE* pStr, + sal_Int32 n, + sal_Int16 nRadix ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX ); + char aBuf[RTL_STR_MAX_VALUEOFINT32]; + char* pBuf = aBuf; + sal_Int32 nLen = 0; + sal_uInt32 nValue; + + /* Radix must be valid */ + if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) ) + nRadix = 10; + + /* is value negative */ + if ( n < 0 ) + { + *pStr = '-'; + pStr++; + nLen++; + nValue = n == SAL_MIN_INT32 ? static_cast<sal_uInt32>(n) : -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; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( valueOfInt64 )( IMPL_RTL_STRCODE* pStr, + sal_Int64 n, + sal_Int16 nRadix ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX ); + char aBuf[RTL_STR_MAX_VALUEOFINT64]; + char* pBuf = aBuf; + sal_Int32 nLen = 0; + sal_uInt64 nValue; + + /* Radix must be valid */ + if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) ) + nRadix = 10; + + /* is value negative */ + if ( n < 0 ) + { + *pStr = '-'; + pStr++; + nLen++; + nValue = n == SAL_MIN_INT64 ? static_cast<sal_uInt64>(n) : -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; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( valueOfUInt64 )( IMPL_RTL_STRCODE* pStr, + sal_uInt64 n, + sal_Int16 nRadix ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX ); + char aBuf[RTL_STR_MAX_VALUEOFUINT64]; + char* pBuf = aBuf; + sal_Int32 nLen = 0; + sal_uInt64 nValue; + + /* Radix must be valid */ + if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) ) + nRadix = 10; + + 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; +} + +/* ----------------------------------------------------------------------- */ + +sal_Bool SAL_CALL IMPL_RTL_STRNAME( toBoolean )( const IMPL_RTL_STRCODE* pStr ) + SAL_THROW_EXTERN_C() +{ + 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; +} + +/* ----------------------------------------------------------------------- */ +namespace { + template<typename T, typename U> T IMPL_RTL_STRNAME( toInt_WithLength )( const IMPL_RTL_STRCODE* pStr, + sal_Int16 nRadix, + sal_Int32 nStrLength ) + { + static_assert(std::numeric_limits<T>::is_signed, "is signed"); + assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX ); + assert( nStrLength >= 0 ); + bool bNeg; + sal_Int16 nDigit; + U n = 0; + const IMPL_RTL_STRCODE* pEnd = pStr + nStrLength; + + if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) ) + nRadix = 10; + + /* Skip whitespaces */ + while ( pStr != pEnd && rtl_ImplIsWhitespace( IMPL_RTL_USTRCODE( *pStr ) ) ) + pStr++; + + if ( *pStr == '-' ) + { + bNeg = true; + pStr++; + } + else + { + if ( *pStr == '+' ) + pStr++; + bNeg = false; + } + + T nDiv; + sal_Int16 nMod; + if ( bNeg ) + { + nDiv = std::numeric_limits<T>::min() / nRadix; + nMod = std::numeric_limits<T>::min() % nRadix; + // Cater for C++03 implementations that round the quotient down + // instead of truncating towards zero as mandated by C++11: + if ( nMod > 0 ) + { + --nDiv; + nMod -= nRadix; + } + nDiv = -nDiv; + nMod = -nMod; + } + else + { + nDiv = std::numeric_limits<T>::max() / nRadix; + nMod = std::numeric_limits<T>::max() % nRadix; + } + + while ( pStr != pEnd ) + { + nDigit = rtl_ImplGetDigit( IMPL_RTL_USTRCODE( *pStr ), nRadix ); + if ( nDigit < 0 ) + break; + assert(nDiv > 0); + if( static_cast<U>( nMod < nDigit ? nDiv-1 : nDiv ) < n ) + return 0; + + n *= nRadix; + n += nDigit; + + pStr++; + } + + if ( bNeg ) + return n == static_cast<U>(std::numeric_limits<T>::min()) + ? std::numeric_limits<T>::min() : -static_cast<T>(n); + else + return static_cast<T>(n); + } +} + +sal_Int32 SAL_CALL IMPL_RTL_STRNAME( toInt32 )( const IMPL_RTL_STRCODE* pStr, + sal_Int16 nRadix ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + return IMPL_RTL_STRNAME( toInt_WithLength )<sal_Int32, sal_uInt32>(pStr, nRadix, IMPL_RTL_STRNAME( getLength )(pStr)); +} + +sal_Int64 SAL_CALL IMPL_RTL_STRNAME( toInt64 )( const IMPL_RTL_STRCODE* pStr, + sal_Int16 nRadix ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + return IMPL_RTL_STRNAME( toInt_WithLength )<sal_Int64, sal_uInt64>(pStr, nRadix, IMPL_RTL_STRNAME( getLength )(pStr)); +} + +sal_Int64 SAL_CALL IMPL_RTL_STRNAME( toInt64_WithLength )( const IMPL_RTL_STRCODE* pStr, + sal_Int16 nRadix, + sal_Int32 nStrLength) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + return IMPL_RTL_STRNAME( toInt_WithLength )<sal_Int64, sal_uInt64>(pStr, nRadix, nStrLength); +} + +/* ----------------------------------------------------------------------- */ +namespace { + template <typename T> T IMPL_RTL_STRNAME( toUInt )( const IMPL_RTL_STRCODE* pStr, + sal_Int16 nRadix ) + { + static_assert(!std::numeric_limits<T>::is_signed, "is not signed"); + assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX ); + sal_Int16 nDigit; + T n = 0; + + if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) ) + nRadix = 10; + + /* Skip whitespaces */ + while ( *pStr && rtl_ImplIsWhitespace( IMPL_RTL_USTRCODE( *pStr ) ) ) + ++pStr; + + // skip optional explicit sign + if ( *pStr == '+' ) + ++pStr; + + T nDiv = std::numeric_limits<T>::max() / nRadix; + sal_Int16 nMod = std::numeric_limits<T>::max() % nRadix; + while ( *pStr ) + { + nDigit = rtl_ImplGetDigit( IMPL_RTL_USTRCODE( *pStr ), nRadix ); + if ( nDigit < 0 ) + break; + if( ( nMod < nDigit ? nDiv-1 : nDiv ) < n ) + return 0; + + n *= nRadix; + n += nDigit; + + ++pStr; + } + + return n; + } +} + +sal_uInt32 SAL_CALL IMPL_RTL_STRNAME( toUInt32 )( const IMPL_RTL_STRCODE* pStr, + sal_Int16 nRadix ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + return IMPL_RTL_STRNAME( toUInt )<sal_uInt32>(pStr, nRadix); +} + +sal_uInt64 SAL_CALL IMPL_RTL_STRNAME( toUInt64 )( const IMPL_RTL_STRCODE* pStr, + sal_Int16 nRadix ) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + return IMPL_RTL_STRNAME( toUInt )<sal_uInt64>(pStr, nRadix); +} + +/* ======================================================================= */ +/* Internal String-Class help functions */ +/* ======================================================================= */ + +IMPL_RTL_STRINGDATA* IMPL_RTL_STRINGNAME( ImplAlloc )( sal_Int32 nLen ) +{ + IMPL_RTL_STRINGDATA * pData + = (sal::static_int_cast< sal_uInt32 >(nLen) + <= ((SAL_MAX_UINT32 - sizeof (IMPL_RTL_STRINGDATA)) + / sizeof (IMPL_RTL_STRCODE))) + ? static_cast<IMPL_RTL_STRINGDATA *>(rtl_allocateString( + sizeof (IMPL_RTL_STRINGDATA) + nLen * sizeof (IMPL_RTL_STRCODE))) + : nullptr; + if (pData != nullptr) { + pData->refCount = 1; + pData->length = nLen; + pData->buffer[nLen] = 0; + } + return pData; +} + +/* ----------------------------------------------------------------------- */ + +static IMPL_RTL_STRCODE* IMPL_RTL_STRINGNAME( ImplNewCopy )( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr, + sal_Int32 nCount ) +{ + assert(nCount >= 0); + IMPL_RTL_STRCODE* pDest; + const IMPL_RTL_STRCODE* pSrc; + IMPL_RTL_STRINGDATA* pData = IMPL_RTL_STRINGNAME( ImplAlloc )( pStr->length ); + OSL_ASSERT(pData != nullptr); + + pDest = pData->buffer; + pSrc = pStr->buffer; + + memcpy( pDest, pSrc, nCount * sizeof(IMPL_RTL_STRCODE)); + + *ppThis = pData; + + RTL_LOG_STRING_NEW( pData ); + return pDest + nCount; +} + +/* ======================================================================= */ +/* String-Class functions */ +/* ======================================================================= */ + +namespace { + +void IMPL_RTL_ACQUIRE(IMPL_RTL_STRINGDATA * pThis) +{ + if (!SAL_STRING_IS_STATIC (pThis)) + osl_atomic_increment( &((pThis)->refCount) ); +} + +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( acquire )( IMPL_RTL_STRINGDATA* pThis ) + SAL_THROW_EXTERN_C() +{ + IMPL_RTL_ACQUIRE( pThis ); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( release )( IMPL_RTL_STRINGDATA* pThis ) + SAL_THROW_EXTERN_C() +{ + if (SAL_UNLIKELY(SAL_STRING_IS_STATIC (pThis))) + return; + +/* OString doesn't have an 'intern' */ +#if IMPL_RTL_IS_USTRING + if (SAL_STRING_IS_INTERN (pThis)) + { + internRelease (pThis); + return; + } +#endif + + if ( !osl_atomic_decrement( &(pThis->refCount) ) ) + { + RTL_LOG_STRING_DELETE( pThis ); + rtl_freeString( pThis ); + } +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( new )( IMPL_RTL_STRINGDATA** ppThis ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + if ( *ppThis) + IMPL_RTL_STRINGNAME( release )( *ppThis ); + + *ppThis = const_cast<IMPL_RTL_STRINGDATA*>(&IMPL_RTL_EMPTYSTRING); +} + +/* ----------------------------------------------------------------------- */ + +IMPL_RTL_STRINGDATA* SAL_CALL IMPL_RTL_STRINGNAME( alloc )( sal_Int32 nLen ) + SAL_THROW_EXTERN_C() +{ + assert(nLen >= 0); + return IMPL_RTL_STRINGNAME( ImplAlloc )( nLen ); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( new_WithLength )( IMPL_RTL_STRINGDATA** ppThis, sal_Int32 nLen ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + assert(nLen >= 0); + if ( nLen <= 0 ) + IMPL_RTL_STRINGNAME( new )( ppThis ); + else + { + if ( *ppThis) + IMPL_RTL_STRINGNAME( release )( *ppThis ); + + *ppThis = IMPL_RTL_STRINGNAME( ImplAlloc )( nLen ); + OSL_ASSERT(*ppThis != nullptr); + (*ppThis)->length = 0; + + IMPL_RTL_STRCODE* pTempStr = (*ppThis)->buffer; + memset(pTempStr, 0, nLen*sizeof(IMPL_RTL_STRCODE)); + } +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( newFromString )( IMPL_RTL_STRINGDATA** ppThis, + const IMPL_RTL_STRINGDATA* pStr ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + assert(pStr); + IMPL_RTL_STRINGDATA* pOrg; + + if ( !pStr->length ) + { + IMPL_RTL_STRINGNAME( new )( ppThis ); + return; + } + + pOrg = *ppThis; + *ppThis = IMPL_RTL_STRINGNAME( ImplAlloc )( pStr->length ); + OSL_ASSERT(*ppThis != nullptr); + rtl_str_ImplCopy( (*ppThis)->buffer, pStr->buffer, pStr->length ); + RTL_LOG_STRING_NEW( *ppThis ); + + /* must be done last, if pStr == *ppThis */ + if ( pOrg ) + IMPL_RTL_STRINGNAME( release )( pOrg ); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( newFromStr )( IMPL_RTL_STRINGDATA** ppThis, + const IMPL_RTL_STRCODE* pCharStr ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + IMPL_RTL_STRINGDATA* pOrg; + sal_Int32 nLen; + + if ( pCharStr ) + { + nLen = IMPL_RTL_STRNAME( getLength )( pCharStr ); + } + else + nLen = 0; + + if ( !nLen ) + { + IMPL_RTL_STRINGNAME( new )( ppThis ); + return; + } + + pOrg = *ppThis; + *ppThis = IMPL_RTL_STRINGNAME( ImplAlloc )( nLen ); + OSL_ASSERT(*ppThis != nullptr); + rtl_str_ImplCopy( (*ppThis)->buffer, pCharStr, nLen ); + RTL_LOG_STRING_NEW( *ppThis ); + + /* must be done last, if pCharStr == *ppThis */ + if ( pOrg ) + IMPL_RTL_STRINGNAME( release )( pOrg ); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( newFromStr_WithLength )( IMPL_RTL_STRINGDATA** ppThis, + const IMPL_RTL_STRCODE* pCharStr, + sal_Int32 nLen ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + assert(nLen >= 0); + IMPL_RTL_STRINGDATA* pOrg; + + if ( !pCharStr || (nLen <= 0) ) + { + IMPL_RTL_STRINGNAME( new )( ppThis ); + return; + } + + pOrg = *ppThis; + *ppThis = IMPL_RTL_STRINGNAME( ImplAlloc )( nLen ); + OSL_ASSERT(*ppThis != nullptr); + rtl_str_ImplCopy( (*ppThis)->buffer, pCharStr, nLen ); + + RTL_LOG_STRING_NEW( *ppThis ); + + /* must be done last, if pCharStr == *ppThis */ + if ( pOrg ) + IMPL_RTL_STRINGNAME( release )( pOrg ); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( newFromSubString )( IMPL_RTL_STRINGDATA** ppThis, + const IMPL_RTL_STRINGDATA* pFrom, + sal_Int32 beginIndex, + sal_Int32 count ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + if ( beginIndex == 0 && count == pFrom->length ) + { + IMPL_RTL_STRINGNAME( assign )( ppThis, const_cast< IMPL_RTL_STRINGDATA * >( pFrom ) ); + return; + } + if ( count < 0 || beginIndex < 0 || beginIndex + count > pFrom->length ) + { + assert(false); // fail fast at least in debug builds + IMPL_RTL_STRINGNAME( newFromLiteral )( ppThis, "!!br0ken!!", 10, 0 ); + return; + } + + IMPL_RTL_STRINGNAME( newFromStr_WithLength )( ppThis, pFrom->buffer + beginIndex, count ); +} + +/* ----------------------------------------------------------------------- */ + +// Used when creating from string literals. +void SAL_CALL IMPL_RTL_STRINGNAME( newFromLiteral )( IMPL_RTL_STRINGDATA** ppThis, + const char* pCharStr, + sal_Int32 nLen, + sal_Int32 allocExtra ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + assert(nLen >= 0); + assert(allocExtra >= 0); + if ( nLen + allocExtra == 0 ) + { + IMPL_RTL_STRINGNAME( new )( ppThis ); + return; + } + + if ( *ppThis ) + IMPL_RTL_STRINGNAME( release )( *ppThis ); + + *ppThis = IMPL_RTL_STRINGNAME( ImplAlloc )( nLen + allocExtra ); + assert( *ppThis != nullptr ); + + (*ppThis)->length = nLen; // fix after possible allocExtra != 0 + (*ppThis)->buffer[nLen] = 0; + IMPL_RTL_STRCODE* pBuffer = (*ppThis)->buffer; + sal_Int32 nCount; + for( nCount = nLen; nCount > 0; --nCount ) + { +#if IMPL_RTL_IS_USTRING + assert(static_cast<unsigned char>(*pCharStr) < 0x80); // ASCII range +#endif + SAL_WARN_IF( (static_cast<unsigned char>(*pCharStr)) == '\0', "rtl.string", + "rtl_uString_newFromLiteral - Found embedded \\0 character" ); + + *pBuffer = *pCharStr; + pBuffer++; + pCharStr++; + } + + RTL_LOG_STRING_NEW( *ppThis ); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( assign )( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + /* must be done at first, if pStr == *ppThis */ + IMPL_RTL_ACQUIRE( pStr ); + + if ( *ppThis ) + IMPL_RTL_STRINGNAME( release )( *ppThis ); + + *ppThis = pStr; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRINGNAME( getLength )( const IMPL_RTL_STRINGDATA* pThis ) + SAL_THROW_EXTERN_C() +{ + assert(pThis); + return pThis->length; +} + +/* ----------------------------------------------------------------------- */ + +IMPL_RTL_STRCODE* SAL_CALL IMPL_RTL_STRINGNAME( getStr )( IMPL_RTL_STRINGDATA * pThis ) + SAL_THROW_EXTERN_C() +{ + assert(pThis); + return pThis->buffer; +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( newConcat )( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pLeft, + IMPL_RTL_STRINGDATA* pRight ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + IMPL_RTL_STRINGDATA* pOrg = *ppThis; + + /* Test for 0-Pointer - if not, change newReplaceStrAt! */ + if ( !pRight || !pRight->length ) + { + *ppThis = pLeft; + IMPL_RTL_ACQUIRE( pLeft ); + } + else if ( !pLeft || !pLeft->length ) + { + *ppThis = pRight; + IMPL_RTL_ACQUIRE( pRight ); + } + else if (pLeft->length + > std::numeric_limits<sal_Int32>::max() - pRight->length) + { + *ppThis = nullptr; + } + else + { + IMPL_RTL_STRINGDATA* pTempStr = IMPL_RTL_STRINGNAME( ImplAlloc )( pLeft->length + pRight->length ); + OSL_ASSERT(pTempStr != nullptr); + *ppThis = pTempStr; + if (*ppThis != nullptr) { + rtl_str_ImplCopy( pTempStr->buffer, pLeft->buffer, pLeft->length ); + rtl_str_ImplCopy( pTempStr->buffer+pLeft->length, pRight->buffer, pRight->length ); + + RTL_LOG_STRING_NEW( *ppThis ); + } + } + + /* must be done last, if left or right == *ppThis */ + if ( pOrg ) + IMPL_RTL_STRINGNAME( release )( pOrg ); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( ensureCapacity )( IMPL_RTL_STRINGDATA** ppThis, + sal_Int32 size ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + IMPL_RTL_STRINGDATA* const pOrg = *ppThis; + if ( pOrg->refCount == 1 && pOrg->length >= size ) + return; + assert( pOrg->length <= size ); // do not truncate + IMPL_RTL_STRINGDATA* pTempStr = IMPL_RTL_STRINGNAME( ImplAlloc )( size ); + rtl_str_ImplCopy( 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 ); + + IMPL_RTL_STRINGNAME( release )( pOrg ); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( newReplaceStrAt )( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr, + sal_Int32 nIndex, + sal_Int32 nCount, + IMPL_RTL_STRINGDATA* pNewSubStr ) + SAL_THROW_EXTERN_C() +{ + 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 */ + IMPL_RTL_STRINGNAME( newConcat )( ppThis, pStr, pNewSubStr ); + return; + } + + /* negative index? */ + if ( nIndex < 0 ) + { + nCount -= nIndex; + nIndex = 0; + } + + /* not more than the String length could be deleted */ + if ( nCount >= pStr->length-nIndex ) + { + nCount = pStr->length-nIndex; + + /* Assign of NewSubStr? */ + if ( !nIndex && (nCount >= pStr->length) ) + { + if ( !pNewSubStr ) + IMPL_RTL_STRINGNAME( new )( ppThis ); + else + IMPL_RTL_STRINGNAME( assign )( ppThis, pNewSubStr ); + return; + } + } + + /* Assign of Str? */ + if ( !nCount && (!pNewSubStr || !pNewSubStr->length) ) + { + IMPL_RTL_STRINGNAME( assign )( ppThis, pStr ); + return; + } + + IMPL_RTL_STRINGDATA* pOrg = *ppThis; + IMPL_RTL_STRCODE* pBuffer; + sal_Int32 nNewLen; + + /* Calculate length of the new string */ + nNewLen = pStr->length-nCount; + if ( pNewSubStr ) + nNewLen += pNewSubStr->length; + + /* Alloc New Buffer */ + *ppThis = IMPL_RTL_STRINGNAME( ImplAlloc )( nNewLen ); + OSL_ASSERT(*ppThis != nullptr); + pBuffer = (*ppThis)->buffer; + if ( nIndex ) + { + rtl_str_ImplCopy( pBuffer, pStr->buffer, nIndex ); + pBuffer += nIndex; + } + if ( pNewSubStr && pNewSubStr->length ) + { + rtl_str_ImplCopy( pBuffer, pNewSubStr->buffer, pNewSubStr->length ); + pBuffer += pNewSubStr->length; + } + rtl_str_ImplCopy( 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 ) + IMPL_RTL_STRINGNAME( release )( pOrg ); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( newReplace )( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr, + IMPL_RTL_STRCODE cOld, + IMPL_RTL_STRCODE cNew ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + assert(pStr); + IMPL_RTL_STRINGDATA* pOrg = *ppThis; + bool bChanged = false; + sal_Int32 nLen = pStr->length; + const IMPL_RTL_STRCODE* pCharStr = pStr->buffer; + + while ( nLen > 0 ) + { + if ( *pCharStr == cOld ) + { + /* Copy String */ + IMPL_RTL_STRCODE* pNewCharStr = IMPL_RTL_STRINGNAME( ImplNewCopy )( ppThis, pStr, pCharStr-pStr->buffer ); + + /* replace/copy rest of the string */ + if ( pNewCharStr ) + { + *pNewCharStr = cNew; + pNewCharStr++; + pCharStr++; + nLen--; + + while ( nLen > 0 ) + { + if ( *pCharStr == cOld ) + *pNewCharStr = cNew; + else + *pNewCharStr = *pCharStr; + + pNewCharStr++; + pCharStr++; + nLen--; + } + } + + bChanged = true; + break; + } + + pCharStr++; + nLen--; + } + + if ( !bChanged ) + { + *ppThis = pStr; + IMPL_RTL_ACQUIRE( pStr ); + } + + RTL_LOG_STRING_NEW( *ppThis ); + /* must be done last, if pStr == *ppThis */ + if ( pOrg ) + IMPL_RTL_STRINGNAME( release )( pOrg ); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( newToAsciiLowerCase )( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + assert(pStr); + IMPL_RTL_STRINGDATA* pOrg = *ppThis; + bool bChanged = false; + sal_Int32 nLen = pStr->length; + const IMPL_RTL_STRCODE* pCharStr = pStr->buffer; + + while ( nLen > 0 ) + { + if ( rtl::isAsciiUpperCase(IMPL_RTL_USTRCODE(*pCharStr)) ) + { + /* Copy String */ + IMPL_RTL_STRCODE* pNewCharStr = IMPL_RTL_STRINGNAME( ImplNewCopy )( ppThis, pStr, pCharStr-pStr->buffer ); + + /* replace/copy rest of the string */ + if ( pNewCharStr ) + { + *pNewCharStr = rtl::toAsciiLowerCase(IMPL_RTL_USTRCODE(*pCharStr)); + pNewCharStr++; + pCharStr++; + nLen--; + + while ( nLen > 0 ) + { + *pNewCharStr = rtl::toAsciiLowerCase(IMPL_RTL_USTRCODE(*pCharStr)); + + pNewCharStr++; + pCharStr++; + nLen--; + } + } + + bChanged = true; + break; + } + + pCharStr++; + nLen--; + } + + if ( !bChanged ) + { + *ppThis = pStr; + IMPL_RTL_ACQUIRE( pStr ); + } + + RTL_LOG_STRING_NEW( *ppThis ); + /* must be done last, if pStr == *ppThis */ + if ( pOrg ) + IMPL_RTL_STRINGNAME( release )( pOrg ); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( newToAsciiUpperCase )( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + assert(pStr); + IMPL_RTL_STRINGDATA* pOrg = *ppThis; + bool bChanged = false; + sal_Int32 nLen = pStr->length; + const IMPL_RTL_STRCODE* pCharStr = pStr->buffer; + + while ( nLen > 0 ) + { + if ( rtl::isAsciiLowerCase(IMPL_RTL_USTRCODE(*pCharStr)) ) + { + /* Copy String */ + IMPL_RTL_STRCODE* pNewCharStr = IMPL_RTL_STRINGNAME( ImplNewCopy )( ppThis, pStr, pCharStr-pStr->buffer ); + + /* replace/copy rest of the string */ + if ( pNewCharStr ) + { + *pNewCharStr = rtl::toAsciiUpperCase(IMPL_RTL_USTRCODE(*pCharStr)); + pNewCharStr++; + pCharStr++; + nLen--; + + while ( nLen > 0 ) + { + *pNewCharStr = rtl::toAsciiUpperCase(IMPL_RTL_USTRCODE(*pCharStr)); + + pNewCharStr++; + pCharStr++; + nLen--; + } + } + + bChanged = true; + break; + } + + pCharStr++; + nLen--; + } + + if ( !bChanged ) + { + *ppThis = pStr; + IMPL_RTL_ACQUIRE( pStr ); + } + + RTL_LOG_STRING_NEW( *ppThis ); + /* must be done last, if pStr == *ppThis */ + if ( pOrg ) + IMPL_RTL_STRINGNAME( release )( pOrg ); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL IMPL_RTL_STRINGNAME( newTrim )( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + assert(pStr); + IMPL_RTL_STRINGDATA* pOrg = *ppThis; + const IMPL_RTL_STRCODE* pCharStr = pStr->buffer; + sal_Int32 nPreSpaces = 0; + sal_Int32 nPostSpaces = 0; + sal_Int32 nLen = pStr->length; + sal_Int32 nIndex = nLen-1; + + while ( (nPreSpaces < nLen) && rtl_ImplIsWhitespace( IMPL_RTL_USTRCODE(*(pCharStr+nPreSpaces)) ) ) + nPreSpaces++; + + while ( (nIndex > nPreSpaces) && rtl_ImplIsWhitespace( IMPL_RTL_USTRCODE(*(pCharStr+nIndex)) ) ) + { + nPostSpaces++; + nIndex--; + } + + if ( !nPreSpaces && !nPostSpaces ) + { + *ppThis = pStr; + IMPL_RTL_ACQUIRE( pStr ); + } + else + { + nLen -= nPostSpaces+nPreSpaces; + *ppThis = IMPL_RTL_STRINGNAME( ImplAlloc )( nLen ); + assert(*ppThis); + rtl_str_ImplCopy( (*ppThis)->buffer, pStr->buffer+nPreSpaces, nLen ); + } + + RTL_LOG_STRING_NEW( *ppThis ); + /* must be done last, if pStr == *ppThis */ + if ( pOrg ) + IMPL_RTL_STRINGNAME( release )( pOrg ); +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL IMPL_RTL_STRINGNAME( getToken )( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr, + sal_Int32 nToken, + IMPL_RTL_STRCODE cTok, + sal_Int32 nIndex ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + assert(pStr); + const IMPL_RTL_STRCODE* pCharStr = pStr->buffer; + const IMPL_RTL_STRCODE* pCharStrStart; + const IMPL_RTL_STRCODE* pOrgCharStr; + sal_Int32 nLen = pStr->length-nIndex; + sal_Int32 nTokCount = 0; + + // Set ppThis to an empty string and return -1 if either nToken or nIndex is + // negative: + if (nIndex < 0) + nToken = -1; + + pCharStr += nIndex; + pOrgCharStr = pCharStr; + pCharStrStart = pCharStr; + while ( nLen > 0 ) + { + if ( *pCharStr == cTok ) + { + nTokCount++; + + if ( nTokCount == nToken ) + pCharStrStart = pCharStr+1; + else + { + if ( nTokCount > nToken ) + break; + } + } + + pCharStr++; + nLen--; + } + + if ( (nToken < 0) || (nTokCount < nToken) || (pCharStr == pCharStrStart) ) + { + IMPL_RTL_STRINGNAME( new )( ppThis ); + if( (nToken < 0) || (nTokCount < nToken ) ) + return -1; + else if( nLen > 0 ) + return nIndex+(pCharStr-pOrgCharStr)+1; + else return -1; + } + else + { + IMPL_RTL_STRINGNAME( newFromStr_WithLength )( ppThis, pCharStrStart, pCharStr-pCharStrStart ); + if ( nLen ) + return nIndex+(pCharStr-pOrgCharStr)+1; + else + return -1; + } +} + +/* 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..975e575db --- /dev/null +++ b/sal/rtl/unload.cxx @@ -0,0 +1,35 @@ +/* -*- 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..441a6c69f --- /dev/null +++ b/sal/rtl/uri.cxx @@ -0,0 +1,893 @@ +/* -*- 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/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 { + +std::size_t const nCharClassSize = 128; + +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 < nCharClassSize && 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) +{ + assert(rtl::isUnicodeCodePoint(nUtf32)); + if (nUtf32 <= 0xFFFF) + { + writeUnicode(pBuffer, pCapacity, static_cast< sal_Unicode >(nUtf32)); + } + else + { + nUtf32 -= 0x10000; + writeUnicode( + pBuffer, pCapacity, + static_cast< sal_Unicode >(nUtf32 >> 10 | 0xD800)); + writeUnicode( + pBuffer, pCapacity, + static_cast< sal_Unicode >((nUtf32 & 0x3FF) | 0xDC00)); + } +} + +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; + if (nUtf32 <= 0xFFFF) + { + aSrc[0] = static_cast< sal_Unicode >(nUtf32); + nSrcSize = 1; + } + else + { + aSrc[0] = static_cast< sal_Unicode >( + ((nUtf32 - 0x10000) >> 10) | 0xD800); + aSrc[1] = static_cast< sal_Unicode >( + ((nUtf32 - 0x10000) & 0x3FF) | 0xDC00); + nSrcSize = 2; + } + + 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 sal_Bool const aCharClass[][nCharClassSize] = { + {false, false, false, false, false, false, false, false,// None + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // !"#$%&' + false, false, false, false, false, false, false, false, // ()*+,-./ + false, false, false, false, false, false, false, false, // 01234567 + false, false, false, false, false, false, false, false, // 89:;<=>? + false, false, false, false, false, false, false, false, // @ABCDEFG + false, false, false, false, false, false, false, false, // HIJKLMNO + false, false, false, false, false, false, false, false, // PQRSTUVW + false, false, false, false, false, false, false, false, // XYZ[\]^_ + false, false, false, false, false, false, false, false, // `abcdefg + false, false, false, false, false, false, false, false, // hijklmno + false, false, false, false, false, false, false, false, // pqrstuvw + false, false, false, false, false, false, false, false}, // xyz{|}~ + {false, false, false, false, false, false, false, false,// Uric + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, false, true, false, true, true, // !"#$%&' + true, true, true, true, true, true, true, true, // ()*+,-./ + true, true, true, true, true, true, true, true, // 01234567 + true, true, true, true, false, true, false, true, // 89:;<=>? + true, true, true, true, true, true, true, true, // @ABCDEFG + true, true, true, true, true, true, true, true, // HIJKLMNO + true, true, true, true, true, true, true, true, // PQRSTUVW + true, true, true, true, false, true, false, true, // XYZ[\]^_ + false, true, true, true, true, true, true, true, // `abcdefg + true, true, true, true, true, true, true, true, // hijklmno + true, true, true, true, true, true, true, true, // pqrstuvw + true, true, true, false, false, false, true, false}, // xyz{|}~ + {false, false, false, false, false, false, false, false,// UricNoSlash + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, false, true, false, true, true, // !"#$%&' + true, true, true, true, true, true, true, false, // ()*+,-./ + true, true, true, true, true, true, true, true, // 01234567 + true, true, true, true, false, true, false, true, // 89:;<=>? + true, true, true, true, true, true, true, true, // @ABCDEFG + true, true, true, true, true, true, true, true, // HIJKLMNO + true, true, true, true, true, true, true, true, // PQRSTUVW + true, true, true, false, false, false, false, true, // XYZ[\]^_ + false, true, true, true, true, true, true, true, // `abcdefg + true, true, true, true, true, true, true, true, // hijklmno + true, true, true, true, true, true, true, true, // pqrstuvw + true, true, true, false, false, false, true, false}, // xyz{|}~ + {false, false, false, false, false, false, false, false,// RelSegment + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, false, true, false, true, true, // !"#$%&' + true, true, true, true, true, true, true, false, // ()*+,-./ + true, true, true, true, true, true, true, true, // 01234567 + true, true, false, true, false, true, false, false, // 89:;<=>? + true, true, true, true, true, true, true, true, // @ABCDEFG + true, true, true, true, true, true, true, true, // HIJKLMNO + true, true, true, true, true, true, true, true, // PQRSTUVW + true, true, true, false, false, false, false, true, // XYZ[\]^_ + false, true, true, true, true, true, true, true, // `abcdefg + true, true, true, true, true, true, true, true, // hijklmno + true, true, true, true, true, true, true, true, // pqrstuvw + true, true, true, false, false, false, true, false}, // xyz{|}~ + {false, false, false, false, false, false, false, false,// RegName + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, false, true, false, true, true, // !"#$%&' + true, true, true, true, true, true, true, false, // ()*+,-./ + true, true, true, true, true, true, true, true, // 01234567 + true, true, true, true, false, true, false, false, // 89:;<=>? + true, true, true, true, true, true, true, true, // @ABCDEFG + true, true, true, true, true, true, true, true, // HIJKLMNO + true, true, true, true, true, true, true, true, // PQRSTUVW + true, true, true, false, false, false, false, true, // XYZ[\]^_ + false, true, true, true, true, true, true, true, // `abcdefg + true, true, true, true, true, true, true, true, // hijklmno + true, true, true, true, true, true, true, true, // pqrstuvw + true, true, true, false, false, false, true, false}, // xyz{|}~ + {false, false, false, false, false, false, false, false,// Userinfo + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, false, true, false, true, true, // !"#$%&' + true, true, true, true, true, true, true, false, // ()*+,-./ + true, true, true, true, true, true, true, true, // 01234567 + true, true, true, true, false, true, false, false, // 89:;<=>? + false, true, true, true, true, true, true, true, // @ABCDEFG + true, true, true, true, true, true, true, true, // HIJKLMNO + true, true, true, true, true, true, true, true, // PQRSTUVW + true, true, true, false, false, false, false, true, // XYZ[\]^_ + false, true, true, true, true, true, true, true, // `abcdefg + true, true, true, true, true, true, true, true, // hijklmno + true, true, true, true, true, true, true, true, // pqrstuvw + true, true, true, false, false, false, true, false}, // xyz{|}~ + {false, false, false, false, false, false, false, false,// Pchar + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, false, true, false, true, true, // !"#$%&' + true, true, true, true, true, true, true, false, // ()*+,-./ + true, true, true, true, true, true, true, true, // 01234567 + true, true, true, false, false, true, false, false, // 89:;<=>? + true, true, true, true, true, true, true, true, // @ABCDEFG + true, true, true, true, true, true, true, true, // HIJKLMNO + true, true, true, true, true, true, true, true, // PQRSTUVW + true, true, true, false, false, false, false, true, // XYZ[\]^_ + false, true, true, true, true, true, true, true, // `abcdefg + true, true, true, true, true, true, true, true, // hijklmno + true, true, true, true, true, true, true, true, // pqrstuvw + true, true, true, false, false, false, true, false}, // xyz{|}~ + {false, false, false, false, false, false, false, false,// UnoParamValue + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, false, true, false, true, true, // !"#$%&' + true, true, true, true, false, true, true, true, // ()*+,-./ + true, true, true, true, true, true, true, true, // 01234567 + true, true, true, false, false, false, false, true, // 89:;<=>? + true, true, true, true, true, true, true, true, // @ABCDEFG + true, true, true, true, true, true, true, true, // HIJKLMNO + true, true, true, true, true, true, true, true, // PQRSTUVW + true, true, true, false, false, false, false, true, // XYZ[\]^_ + false, true, true, true, true, true, true, true, // `abcdefg + true, true, true, true, true, true, true, true, // hijklmno + true, true, true, true, true, true, true, true, // pqrstuvw + true, true, true, false, false, false, true, false}}; // xyz{|}~ + + assert( + (eCharClass >= 0 + && (sal::static_int_cast< std::size_t >(eCharClass) + < SAL_N_ELEMENTS(aCharClass)))); // bad eCharClass + return aCharClass[eCharClass]; +} + +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..53e178e58 --- /dev/null +++ b/sal/rtl/ustrbuf.cxx @@ -0,0 +1,268 @@ +/* -*- 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 + +void SAL_CALL rtl_uStringbuffer_newFromStr_WithLength( rtl_uString ** newStr, + const sal_Unicode * value, + sal_Int32 count) +{ + assert(newStr); + assert(count >= 0); + if (!value) + { + rtl_uString_new_WithLength( newStr, 16 ); + return; + } + + // use raw alloc to avoid overwriting the buffer twice + if ( *newStr) + rtl_uString_release( *newStr ); + *newStr = rtl_uString_ImplAlloc( count + 16 ); + (*newStr)->length = count; + memcpy( (*newStr)->buffer, value, count * sizeof(sal_Unicode) ); + memset( (*newStr)->buffer + count, 0, 16 * sizeof(sal_Unicode) ); + RTL_LOG_STRING_NEW( *newStr ); +} + +rtl_uString * SAL_CALL rtl_uStringBuffer_refReturn( rtl_uString * pThis ) +{ + RTL_LOG_STRING_NEW( pThis ); + rtl_uString_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_uString_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 ) +{ + assert(newStr); + assert(capacity >= 0); + assert(oldStr); + sal_Int32 newCapacity = capacity; + + if (newCapacity < oldStr->length) + newCapacity = oldStr->length; + + rtl_uString_new_WithLength( newStr, newCapacity ); + + if (oldStr->length > 0) { + (*newStr)->length = oldStr->length; + memcpy( (*newStr)->buffer, oldStr->buffer, oldStr->length * sizeof(sal_Unicode)); + } + RTL_LOG_STRING_NEW( *newStr ); + return newCapacity; +} + +void SAL_CALL rtl_uStringbuffer_ensureCapacity + (rtl_uString ** This, sal_Int32* capacity, sal_Int32 minimumCapacity) +{ + assert(This); + assert(capacity && *capacity >= 0); + assert(minimumCapacity >= 0); + if (minimumCapacity <= *capacity) + return; + + rtl_uString * pTmp = *This; + rtl_uString * pNew = nullptr; + auto nLength = (*This)->length; + *capacity = (nLength + 1) * 2; + if (minimumCapacity > *capacity) + /* still lower, set to the minimum capacity */ + *capacity = minimumCapacity; + + // use raw alloc to avoid overwriting the buffer twice + pNew = rtl_uString_ImplAlloc( *capacity ); + pNew->length = nLength; + *This = pNew; + + memcpy( (*This)->buffer, pTmp->buffer, nLength * sizeof(sal_Unicode) ); + memset( (*This)->buffer + nLength, 0, (*capacity - nLength) * sizeof(sal_Unicode) ); + + RTL_LOG_STRING_NEW( pTmp ); // with accurate contents + rtl_uString_release( pTmp ); +} + +void SAL_CALL rtl_uStringbuffer_insert( rtl_uString ** This, + sal_Int32 * capacity, + sal_Int32 offset, + const sal_Unicode * str, + sal_Int32 len) +{ + assert(This); + assert(capacity && *capacity >= 0); + assert(offset >= 0 && offset <= (**This).length); + assert(len >= 0); + sal_Int32 nOldLen; + sal_Unicode * pBuf; + sal_Int32 n; + if( len == 0 ) + return; + + if (*capacity < (*This)->length + len) + rtl_uStringbuffer_ensureCapacity( This, capacity, (*This)->length + len ); + + nOldLen = (*This)->length; + pBuf = (*This)->buffer; + + /* copy the tail */ + n = (nOldLen - offset); + if( n == 1 ) + /* optimized for 1 character */ + pBuf[offset + len] = pBuf[offset]; + else if( n > 1 ) + memmove( pBuf + offset + len, pBuf + offset, n * sizeof(sal_Unicode) ); + + /* insert the new characters */ + if( str != nullptr ) + { + if( len == 1 ) + /* optimized for 1 character */ + pBuf[offset] = *str; + else + memcpy( pBuf + offset, str, len * sizeof(sal_Unicode) ); + } + (*This)->length = nOldLen + len; + pBuf[ nOldLen + len ] = 0; +} + +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; + OSL_ASSERT(rtl::isUnicodeScalarValue(c)); + if (c <= 0xFFFF) { + buf[0] = static_cast<sal_Unicode>(c); + len = 1; + } else { + c -= 0x10000; + buf[0] = static_cast<sal_Unicode>((c >> 10) | 0xD800); + buf[1] = static_cast<sal_Unicode>((c & 0x3FF) | 0xDC00); + len = 2; + } + rtl_uStringbuffer_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) +{ + assert(This); + assert(capacity && *capacity >= 0); + assert(offset >= 0 && offset <= (**This).length); + assert(len == 0 || str != nullptr); + assert(len >= 0); + sal_Int32 nOldLen; + sal_Unicode * pBuf; + sal_Int32 n; + if( len == 0 ) + return; + + if (*capacity < (*This)->length + len) + rtl_uStringbuffer_ensureCapacity( This, capacity, (*This)->length + len ); + + nOldLen = (*This)->length; + pBuf = (*This)->buffer; + + /* copy the tail */ + n = (nOldLen - offset); + if( n == 1 ) + /* optimized for 1 character */ + pBuf[offset + len] = pBuf[offset]; + else if( n > 1 ) + memmove( pBuf + offset + len, pBuf + offset, n * sizeof(sal_Unicode) ); + + /* insert the new characters */ + for( n = 0; n < len; n++ ) + { + /* Check ASCII range */ + OSL_ENSURE( (*str & 0x80) == 0, "Found ASCII char > 127"); + + pBuf[offset + n] = static_cast<sal_Unicode>(*(str++)); + } + + (*This)->length = nOldLen + len; + pBuf[ nOldLen + len ] = 0; +} + +/************************************************************************* + * rtl_uStringbuffer_remove + */ +void SAL_CALL rtl_uStringbuffer_remove( rtl_uString ** This, + sal_Int32 start, + sal_Int32 len ) +{ + assert(This); + assert(start >= 0 && start <= (**This).length); + assert(len >= 0); + sal_Int32 nTailLen; + sal_Unicode * pBuf; + + if (len > (*This)->length - start) + len = (*This)->length - start; + + //remove nothing + if (!len) + return; + + pBuf = (*This)->buffer; + nTailLen = (*This)->length - ( start + len ); + + if (nTailLen) + { + /* move the tail */ + memmove(pBuf + start, pBuf + start + len, nTailLen * sizeof(sal_Unicode)); + } + + (*This)->length-=len; + pBuf[ (*This)->length ] = 0; +} + +/* 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..01fea63e1 --- /dev/null +++ b/sal/rtl/ustring.cxx @@ -0,0 +1,1562 @@ +/* -*- 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 <osl/diagnose.h> +#include <osl/interlck.h> +#include <rtl/alloc.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> + +/* ======================================================================= */ + +/* static data to be referenced by all empty strings + * the refCount is predefined to 1 and must never become 0 ! + */ +static rtl_uString const aImplEmpty_rtl_uString = +{ + sal_Int32(SAL_STRING_INTERN_FLAG|SAL_STRING_STATIC_FLAG|1), /*sal_Int32 refCount; */ + 0, /*sal_Int32 length; */ + { 0 } /*sal_Unicode buffer[1];*/ +}; + +/* ======================================================================= */ +/* These macros are for the "poor-man templates" included from + * the strtmpl.cxx just below, used to share code between here and + * string.cxx + */ + +#define IMPL_RTL_IS_USTRING 1 + +#define IMPL_RTL_STRCODE sal_Unicode +#define IMPL_RTL_USTRCODE( c ) (c) +#define IMPL_RTL_STRNAME( n ) rtl_ustr_ ## n + +#define IMPL_RTL_STRINGNAME( n ) rtl_uString_ ## n +#define IMPL_RTL_STRINGDATA rtl_uString +#define IMPL_RTL_EMPTYSTRING aImplEmpty_rtl_uString + +static void internRelease (rtl_uString *pThis); + +#if USE_SDT_PROBES +#define RTL_LOG_STRING_BITS 16 +#endif + +/* ======================================================================= */ + +/* Include String/UString template code */ + +#include "strtmpl.cxx" + +#undef IMPL_RTL_EMPTYSTRING +#undef IMPL_RTL_IS_USTRING +#undef IMPL_RTL_STRCODE +#undef IMPL_RTL_STRINGDATA +#undef IMPL_RTL_STRINGNAME +#undef IMPL_RTL_STRNAME +#undef IMPL_RTL_USTRCODE +#undef RTL_LOG_STRING_BITS + +sal_Int32 rtl_ustr_indexOfAscii_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_Unicode const* end = str + len; + sal_Unicode const* cursor = str; + + while(cursor < end) + { + cursor = std::char_traits<sal_Unicode>::find(cursor, end - cursor, *subStr); + if(!cursor || (end - cursor < subLen)) + { + /* no enough left to actually have a match */ + break; + } + /* now it is worth trying a full match */ + if (rtl_ustr_asciil_reverseEquals_WithLength(cursor, subStr, subLen)) + { + return cursor - str; + } + cursor += 1; + } + } + return -1; +} + +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() +{ + assert(pStr); + rtl_uString * pResult = nullptr; + sal_Int32 nLen; + rtl_math_doubleToUString( + &pResult, nullptr, 0, f, rtl_math_StringFormat_G, + RTL_USTR_MAX_VALUEOFFLOAT - RTL_CONSTASCII_LENGTH("-x.E-xxx"), '.', nullptr, + 0, true); + nLen = pResult->length; + OSL_ASSERT(nLen < RTL_USTR_MAX_VALUEOFFLOAT); + memcpy(pStr, pResult->buffer, (nLen + 1) * sizeof(sal_Unicode)); + rtl_uString_release(pResult); + return nLen; +} + +sal_Int32 SAL_CALL rtl_ustr_valueOfDouble(sal_Unicode * pStr, double d) + SAL_THROW_EXTERN_C() +{ + assert(pStr); + rtl_uString * pResult = nullptr; + sal_Int32 nLen; + rtl_math_doubleToUString( + &pResult, nullptr, 0, d, rtl_math_StringFormat_G, + RTL_USTR_MAX_VALUEOFDOUBLE - RTL_CONSTASCII_LENGTH("-x.E-xxx"), '.', nullptr, + 0, true); + nLen = pResult->length; + OSL_ASSERT(nLen < RTL_USTR_MAX_VALUEOFDOUBLE); + memcpy(pStr, pResult->buffer, (nLen + 1) * sizeof(sal_Unicode)); + rtl_uString_release(pResult); + return nLen; +} + +namespace { + +// Avoid -fsanitize=undefined warning e.g. "runtime error: value 1e+99 is +// outside the range of representable values of type 'float'": +float doubleToFloat(double x) { + 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); +} + +} + +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() +{ + assert(pStr1); + assert(pStr2); + sal_Int32 nRet; + for (;;) + { + nRet = static_cast<sal_Int32>(*pStr1)- + static_cast<sal_Int32>(static_cast<unsigned char>(*pStr2)); + if (!(nRet == 0 && *pStr2 )) + break; + /* Check ASCII range */ + SAL_WARN_IF( (static_cast<unsigned char>(*pStr2)) > 127, "rtl.string", + "rtl_ustr_ascii_compare - Found char > 127" ); + pStr1++; + pStr2++; + } + + return nRet; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL rtl_ustr_ascii_compare_WithLength( const sal_Unicode* pStr1, + sal_Int32 nStr1Len, + const char* pStr2 ) + SAL_THROW_EXTERN_C() +{ + assert(pStr1); + assert(nStr1Len >= 0); + assert(pStr2); + sal_Int32 nRet = 0; + for (;;) + { + nRet = (nStr1Len ? static_cast<sal_Int32>(*pStr1) : 0) - + static_cast<sal_Int32>(static_cast<unsigned char>(*pStr2)); + if (!(nRet == 0 && nStr1Len && *pStr2 )) + break; + /* Check ASCII range */ + SAL_WARN_IF( (static_cast<unsigned char>(*pStr2)) > 127, "rtl.string", + "rtl_ustr_ascii_compare_WithLength - Found char > 127" ); + pStr1++; + pStr2++; + nStr1Len--; + } + + return nRet; +} + +/* ----------------------------------------------------------------------- */ + +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() +{ + assert(nStr1Len >= 0); + assert(nShortenedLength >= 0); + const sal_Unicode* pStr1End = pStr1 + nStr1Len; + sal_Int32 nRet; + while ( (nShortenedLength > 0) && + (pStr1 < pStr1End) && *pStr2 ) + { + /* Check ASCII range */ + SAL_WARN_IF( (static_cast<unsigned char>(*pStr2)) > 127, "rtl.string", + "rtl_ustr_ascii_shortenedCompare_WithLength - Found char > 127" ); + + nRet = static_cast<sal_Int32>(*pStr1)- + static_cast<sal_Int32>(static_cast<unsigned char>(*pStr2)); + if ( nRet != 0 ) + return nRet; + + nShortenedLength--; + pStr1++; + pStr2++; + } + + if ( nShortenedLength <= 0 ) + return 0; + + if ( *pStr2 ) + { + OSL_ENSURE( pStr1 == pStr1End, "pStr1 == pStr1End failed" ); + // first is a substring of the second string => less (negative value) + nRet = -1; + } + else + { + // greater or equal + nRet = pStr1End - pStr1; + } + + return nRet; +} + +/* ----------------------------------------------------------------------- */ + +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() +{ + assert(nStr1Len >= 0 && nStr2Len >= 0); + const sal_Unicode* pStr1Run = pStr1+nStr1Len; + const char* pStr2Run = pStr2+nStr2Len; + sal_Int32 nRet; + while ( (pStr1 < pStr1Run) && (pStr2 < pStr2Run) ) + { + /* Check ASCII range */ + SAL_WARN_IF( (static_cast<unsigned char>(*pStr2)) > 127, "rtl.string", + "rtl_ustr_asciil_reverseCompare_WithLength - Found char > 127" ); + pStr1Run--; + pStr2Run--; + nRet = static_cast<sal_Int32>(*pStr1Run)- static_cast<sal_Int32>(*pStr2Run); + if ( nRet ) + return nRet; + } + + return nStr1Len - nStr2Len; +} + +/* ----------------------------------------------------------------------- */ + +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 ) + { + /* Check ASCII range */ + SAL_WARN_IF( (static_cast<unsigned char>(*pStr2)) > 127, "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() +{ + assert(pStr1); + assert(pStr2); + sal_Int32 nRet; + sal_Int32 c1; + sal_Int32 c2; + do + { + /* Check ASCII range */ + SAL_WARN_IF( (static_cast<unsigned char>(*pStr2)) > 127, "rtl.string", + "rtl_ustr_ascii_compareIgnoreAsciiCase - Found char > 127" ); + /* If character between 'A' and 'Z', then convert it to lowercase */ + c1 = static_cast<sal_Int32>(*pStr1); + c2 = static_cast<sal_Int32>(static_cast<unsigned char>(*pStr2)); + if ( (c1 >= 65) && (c1 <= 90) ) + c1 += 32; + if ( (c2 >= 65) && (c2 <= 90) ) + c2 += 32; + nRet = c1-c2; + if ( nRet != 0 ) + return nRet; + + pStr1++; + pStr2++; + } + while ( c2 ); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( const sal_Unicode* pStr1, + sal_Int32 nStr1Len, + const char* pStr2 ) + SAL_THROW_EXTERN_C() +{ + assert(nStr1Len >= 0); + assert(pStr2); + sal_Int32 nRet; + sal_Int32 c1; + sal_Int32 c2; + do + { + /* Check ASCII range */ + SAL_WARN_IF( (static_cast<unsigned char>(*pStr2)) > 127, "rtl.string", + "rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength - Found char > 127" ); + if ( !nStr1Len ) + return *pStr2 == '\0' ? 0 : -1; + + /* If character between 'A' and 'Z', then convert it to lowercase */ + c1 = static_cast<sal_Int32>(*pStr1); + c2 = static_cast<sal_Int32>(static_cast<unsigned char>(*pStr2)); + if ( (c1 >= 65) && (c1 <= 90) ) + c1 += 32; + if ( (c2 >= 65) && (c2 <= 90) ) + c2 += 32; + nRet = c1-c2; + if ( nRet != 0 ) + return nRet; + + pStr1++; + pStr2++; + nStr1Len--; + } + while( c2 ); + + return 0; +} + +sal_Int32 rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( + sal_Unicode const * first, sal_Int32 firstLen, + char const * second, sal_Int32 secondLen) SAL_THROW_EXTERN_C() +{ + assert(firstLen >= 0 && secondLen >= 0); + sal_Int32 i; + sal_Int32 len = std::min(firstLen, secondLen); + for (i = 0; i < len; ++i) { + /* Check ASCII range */ + SAL_WARN_IF( (static_cast<unsigned char>(*second)) > 127, "rtl.string", + "rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths - Found char > 127" ); + sal_Int32 c1 = *first++; + sal_Int32 c2 = static_cast<unsigned char>(*second++); + sal_Int32 d; + if (c1 >= 65 && c1 <= 90) { + c1 += 32; + } + if (c2 >= 65 && c2 <= 90) { + c2 += 32; + } + d = c1 - c2; + if (d != 0) { + return d; + } + } + return firstLen - secondLen; +} + +/* ----------------------------------------------------------------------- */ + +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() +{ + assert(nStr1Len >= 0); + assert(nShortenedLength >= 0); + const sal_Unicode* pStr1End = pStr1 + nStr1Len; + sal_Int32 nRet; + sal_Int32 c1; + sal_Int32 c2; + while ( (nShortenedLength > 0) && + (pStr1 < pStr1End) && *pStr2 ) + { + /* Check ASCII range */ + SAL_WARN_IF( (static_cast<unsigned char>(*pStr2)) > 127, "rtl.string", + "rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength - Found char > 127" ); + + /* If character between 'A' and 'Z', then convert it to lowercase */ + c1 = static_cast<sal_Int32>(*pStr1); + c2 = static_cast<sal_Int32>(static_cast<unsigned char>(*pStr2)); + if ( (c1 >= 65) && (c1 <= 90) ) + c1 += 32; + if ( (c2 >= 65) && (c2 <= 90) ) + c2 += 32; + nRet = c1-c2; + if ( nRet != 0 ) + return nRet; + + nShortenedLength--; + pStr1++; + pStr2++; + } + + if ( nShortenedLength <= 0 ) + return 0; + + if ( *pStr2 ) + { + OSL_ENSURE( pStr1 == pStr1End, "pStr1 == pStr1End failed" ); + // first is a substring of the second string => less (negative value) + nRet = -1; + } + else + { + // greater or equal + nRet = pStr1End - pStr1; + } + + return nRet; +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL rtl_uString_newFromAscii( rtl_uString** ppThis, + const char* pCharStr ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + sal_Int32 nLen; + + if ( pCharStr ) + { + const char* pTempStr = pCharStr; + while( *pTempStr ) + pTempStr++; + nLen = pTempStr-pCharStr; + } + else + nLen = 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 ) + { + 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) +{ + assert(newString != nullptr); + assert(left != nullptr); + assert(right != nullptr); + assert(rightLength >= 0); + if (left->length > std::numeric_limits<sal_Int32>::max() - rightLength) { +#if !defined(__COVERITY__) + throw std::length_error("rtl_uString_newConcatAsciiL"); +#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 + } + sal_Int32 n = left->length + rightLength; + rtl_uString_assign(newString, left); + rtl_uString_ensureCapacity(newString, n); + sal_Unicode * p = (*newString)->buffer + (*newString)->length; + for (sal_Int32 i = 0; i != rightLength; ++i) { + p[i] = static_cast<unsigned char>(right[i]); + } + (*newString)->buffer[n] = 0; + (*newString)->length = n; +} + +void rtl_uString_newConcatUtf16L( + rtl_uString ** newString, rtl_uString * left, sal_Unicode const * right, + sal_Int32 rightLength) +{ + assert(newString != nullptr); + assert(left != nullptr); + assert(right != nullptr); + assert(rightLength >= 0); + if (left->length > std::numeric_limits<sal_Int32>::max() - rightLength) { +#if !defined(__COVERITY__) + throw std::length_error("rtl_uString_newConcatUtf16L"); +#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 + } + sal_Int32 n = left->length + rightLength; + rtl_uString_assign(newString, left); + rtl_uString_ensureCapacity(newString, n); + memcpy( + (*newString)->buffer + (*newString)->length, right, + rightLength * sizeof (sal_Unicode)); + (*newString)->buffer[n] = 0; + (*newString)->length = n; +} + +/* ======================================================================= */ + +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_ImplCopy(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)) + { + IMPL_RTL_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; + break; + } + 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( (static_cast<unsigned char>(str[i])) > 127, "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 ); +} + +static void +internRelease (rtl_uString *pThis) +{ + oslMutex pPoolMutex; + + rtl_uString *pFree = nullptr; + if ( SAL_STRING_REFCOUNT( + osl_atomic_decrement( &(pThis->refCount) ) ) == 0) + { + RTL_LOG_STRING_INTERN_DELETE(pThis); + 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(str != nullptr); + assert(index != nullptr); + assert(*index >= 0 && *index <= str->length); + assert(from != nullptr); + assert(to != nullptr); + sal_Int32 i = rtl_ustr_indexOfStr_WithLength( + str->buffer + *index, str->length - *index, from->buffer, from->length); + if (i == -1) { + rtl_uString_assign(newStr, str); + } else { + assert(i <= str->length - *index); + i += *index; + assert(from->length <= str->length); + if (str->length - from->length > SAL_MAX_INT32 - to->length) { + std::abort(); + } + sal_Int32 n = str->length - from->length + to->length; + rtl_uString_acquire(str); // in case *newStr == str + rtl_uString_new_WithLength(newStr, n); + if (n != 0) { + (*newStr)->length = n; + assert(i >= 0 && i < str->length); + memcpy( + (*newStr)->buffer, str->buffer, i * sizeof (sal_Unicode)); + memcpy( + (*newStr)->buffer + i, to->buffer, + to->length * sizeof (sal_Unicode)); + memcpy( + (*newStr)->buffer + i + to->length, + str->buffer + i + from->length, + (str->length - i - from->length) * sizeof (sal_Unicode)); + } + rtl_uString_release(str); + } + *index = i; +} + +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(str != nullptr); + assert(index != nullptr); + assert(*index >= 0 && *index <= str->length); + assert(fromLength >= 0); + assert(to != nullptr); + sal_Int32 i = rtl_ustr_indexOfAscii_WithLength( + str->buffer + *index, str->length - *index, from, fromLength); + if (i == -1) { + rtl_uString_assign(newStr, str); + } else { + assert(i <= str->length - *index); + i += *index; + assert(fromLength <= str->length); + if (str->length - fromLength > SAL_MAX_INT32 - to->length) { + std::abort(); + } + sal_Int32 n = str->length - fromLength + to->length; + rtl_uString_acquire(str); // in case *newStr == str + rtl_uString_new_WithLength(newStr, n); + if (n != 0) { + (*newStr)->length = n; + assert(i >= 0 && i < str->length); + memcpy( + (*newStr)->buffer, str->buffer, i * sizeof (sal_Unicode)); + memcpy( + (*newStr)->buffer + i, to->buffer, + to->length * sizeof (sal_Unicode)); + memcpy( + (*newStr)->buffer + i + to->length, + str->buffer + i + fromLength, + (str->length - i - fromLength) * sizeof (sal_Unicode)); + } + rtl_uString_release(str); + } + *index = i; +} + +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(str != nullptr); + assert(index != nullptr); + assert(*index >= 0 && *index <= str->length); + assert(from != nullptr); + assert(toLength >= 0); + sal_Int32 i = rtl_ustr_indexOfStr_WithLength( + str->buffer + *index, str->length - *index, from->buffer, from->length); + if (i == -1) { + rtl_uString_assign(newStr, str); + } else { + assert(i <= str->length - *index); + i += *index; + assert(from->length <= str->length); + if (str->length - from->length > SAL_MAX_INT32 - toLength) { + std::abort(); + } + sal_Int32 n = str->length - from->length + toLength; + rtl_uString_acquire(str); // in case *newStr == str + rtl_uString_new_WithLength(newStr, n); + if (n != 0) { + (*newStr)->length = n; + assert(i >= 0 && i < str->length); + memcpy( + (*newStr)->buffer, str->buffer, i * sizeof (sal_Unicode)); + for (sal_Int32 j = 0; j != toLength; ++j) { + assert(static_cast< unsigned char >(to[j]) <= 0x7F); + (*newStr)->buffer[i + j] = to[j]; + } + memcpy( + (*newStr)->buffer + i + toLength, + str->buffer + i + from->length, + (str->length - i - from->length) * sizeof (sal_Unicode)); + } + rtl_uString_release(str); + } + *index = i; +} + +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(str != nullptr); + assert(index != nullptr); + assert(*index >= 0 && *index <= str->length); + assert(fromLength >= 0); + assert(to != nullptr); + assert(toLength >= 0); + sal_Int32 i = rtl_ustr_indexOfAscii_WithLength( + str->buffer + *index, str->length - *index, from, fromLength); + if (i == -1) { + rtl_uString_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(); + } + sal_Int32 n = str->length - fromLength + toLength; + rtl_uString_acquire(str); // in case *newStr == str + rtl_uString_new_WithLength(newStr, n); + if (n != 0) { + (*newStr)->length = n; + assert(i >= 0 && i < str->length); + memcpy( + (*newStr)->buffer, str->buffer, i * sizeof (sal_Unicode)); + for (sal_Int32 j = 0; j != toLength; ++j) { + assert(static_cast< unsigned char >(to[j]) <= 0x7F); + (*newStr)->buffer[i + j] = to[j]; + } + memcpy( + (*newStr)->buffer + i + toLength, + str->buffer + i + fromLength, + (str->length - i - fromLength) * sizeof (sal_Unicode)); + } + rtl_uString_release(str); + } + *index = i; +} + +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(str != nullptr); + assert(index != nullptr); + assert(*index >= 0 && *index <= str->length); + assert(fromLength >= 0); + assert(to != nullptr); + assert(toLength >= 0); + sal_Int32 i = rtl_ustr_indexOfAscii_WithLength( + str->buffer + *index, str->length - *index, from, fromLength); + if (i == -1) { + rtl_uString_assign(newStr, str); + } else { + assert(i <= str->length - *index); + i += *index; + assert(fromLength <= str->length); + if (str->length - fromLength > SAL_MAX_INT32 - toLength) { + rtl_uString_release(*newStr); + *newStr = nullptr; + } else { + sal_Int32 n = str->length - fromLength + toLength; + rtl_uString_acquire(str); // in case *newStr == str + rtl_uString_new_WithLength(newStr, n); + if (n != 0 && /*TODO:*/ *newStr != nullptr) { + (*newStr)->length = n; + assert(i >= 0 && i < str->length); + memcpy( + (*newStr)->buffer, str->buffer, i * sizeof (sal_Unicode)); + memcpy( + (*newStr)->buffer + i, to, toLength * sizeof (sal_Unicode)); + memcpy( + (*newStr)->buffer + i + toLength, + str->buffer + i + fromLength, + (str->length - i - fromLength) * sizeof (sal_Unicode)); + } + rtl_uString_release(str); + } + } + *index = i; +} + +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(str != nullptr); + assert(index != nullptr); + assert(*index >= 0 && *index <= str->length); + assert(fromLength >= 0); + assert(to != nullptr); + assert(toLength >= 0); + sal_Int32 i = rtl_ustr_indexOfStr_WithLength( + str->buffer + *index, str->length - *index, from, fromLength); + if (i == -1) { + rtl_uString_assign(newStr, str); + } else { + assert(i <= str->length - *index); + i += *index; + assert(fromLength <= str->length); + if (str->length - fromLength > SAL_MAX_INT32 - toLength) { + rtl_uString_release(*newStr); + *newStr = nullptr; + } else { + sal_Int32 n = str->length - fromLength + toLength; + rtl_uString_acquire(str); // in case *newStr == str + rtl_uString_new_WithLength(newStr, n); + if (n != 0 && /*TODO:*/ *newStr != nullptr) { + (*newStr)->length = n; + assert(i >= 0 && i < str->length); + memcpy( + (*newStr)->buffer, str->buffer, i * sizeof (sal_Unicode)); + for (sal_Int32 j = 0; j != toLength; ++j) { + assert(static_cast< unsigned char >(to[j]) <= 0x7F); + (*newStr)->buffer[i + j] = to[j]; + } + memcpy( + (*newStr)->buffer + i + toLength, + str->buffer + i + fromLength, + (str->length - i - fromLength) * sizeof (sal_Unicode)); + } + rtl_uString_release(str); + } + } + *index = i; +} + +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(str != nullptr); + assert(index != nullptr); + assert(*index >= 0 && *index <= str->length); + assert(fromLength >= 0); + assert(to != nullptr); + assert(toLength >= 0); + sal_Int32 i = rtl_ustr_indexOfStr_WithLength( + str->buffer + *index, str->length - *index, from, fromLength); + if (i == -1) { + rtl_uString_assign(newStr, str); + } else { + assert(i <= str->length - *index); + i += *index; + assert(fromLength <= str->length); + if (str->length - fromLength > SAL_MAX_INT32 - toLength) { + rtl_uString_release(*newStr); + *newStr = nullptr; + } else { + sal_Int32 n = str->length - fromLength + toLength; + rtl_uString_acquire(str); // in case *newStr == str + rtl_uString_new_WithLength(newStr, n); + if (n != 0 && /*TODO:*/ *newStr != nullptr) { + (*newStr)->length = n; + assert(i >= 0 && i < str->length); + memcpy( + (*newStr)->buffer, str->buffer, i * sizeof (sal_Unicode)); + memcpy( + (*newStr)->buffer + i, to, toLength * sizeof (sal_Unicode)); + memcpy( + (*newStr)->buffer + i + toLength, + str->buffer + i + fromLength, + (str->length - i - fromLength) * sizeof (sal_Unicode)); + } + rtl_uString_release(str); + } + } + *index = i; +} + +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_assign(newStr, str); + for (sal_Int32 i = fromIndex;; i += to->length) { + rtl_uString_newReplaceFirst(newStr, *newStr, from, to, &i); + if (i == -1) { + break; + } + } +} + +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_assign(newStr, str); + for (sal_Int32 i = 0;; i += to->length) { + rtl_uString_newReplaceFirstAsciiL( + newStr, *newStr, from, fromLength, to, &i); + if (i == -1) { + break; + } + } +} + +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_assign(newStr, str); + for (sal_Int32 i = 0;; i += toLength) { + rtl_uString_newReplaceFirstToAsciiL( + newStr, *newStr, from, to, toLength, &i); + if (i == -1) { + break; + } + } +} + +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() +{ + assert(toLength >= 0); + rtl_uString_assign(newStr, str); + for (sal_Int32 i = 0;; i += toLength) { + rtl_uString_newReplaceFirstAsciiLAsciiL( + newStr, *newStr, from, fromLength, to, toLength, &i); + if (i == -1) { + break; + } + } +} + +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() +{ + assert(toLength >= 0); + rtl_uString_assign(newStr, str); + for (sal_Int32 i = 0;; i += toLength) { + rtl_uString_newReplaceFirstAsciiLUtf16L( + newStr, *newStr, from, fromLength, to, toLength, &i); + if (i == -1 || *newStr == nullptr) { + break; + } + } +} + +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() +{ + assert(toLength >= 0); + rtl_uString_assign(newStr, str); + for (sal_Int32 i = 0;; i += toLength) { + rtl_uString_newReplaceFirstUtf16LAsciiL( + newStr, *newStr, from, fromLength, to, toLength, &i); + if (i == -1 || *newStr == nullptr) { + break; + } + } +} + +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() +{ + assert(toLength >= 0); + rtl_uString_assign(newStr, str); + for (sal_Int32 i = 0;; i += toLength) { + rtl_uString_newReplaceFirstUtf16LUtf16L( + newStr, *newStr, from, fromLength, to, toLength, &i); + if (i == -1 || *newStr == nullptr) { + break; + } + } +} + +/* 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: */ |