diff options
Diffstat (limited to 'sal/rtl')
-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 | 97 | ||||
-rw-r--r-- | sal/rtl/alloc_global.cxx | 79 | ||||
-rw-r--r-- | sal/rtl/alloc_impl.hxx | 219 | ||||
-rw-r--r-- | sal/rtl/bootstrap.cxx | 992 | ||||
-rw-r--r-- | sal/rtl/byteseq.cxx | 246 | ||||
-rw-r--r-- | sal/rtl/cipher.cxx | 1433 | ||||
-rw-r--r-- | sal/rtl/cmdargs.cxx | 103 | ||||
-rw-r--r-- | sal/rtl/crc.cxx | 45 | ||||
-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 | 136 | ||||
-rw-r--r-- | sal/rtl/math.cxx | 828 | ||||
-rw-r--r-- | sal/rtl/random.cxx | 310 | ||||
-rw-r--r-- | sal/rtl/rtl_process.cxx | 56 | ||||
-rw-r--r-- | sal/rtl/strbuf.cxx | 83 | ||||
-rw-r--r-- | sal/rtl/strimp.cxx | 90 | ||||
-rw-r--r-- | sal/rtl/strimp.hxx | 92 | ||||
-rw-r--r-- | sal/rtl/string.cxx | 667 | ||||
-rw-r--r-- | sal/rtl/strtmpl.hxx | 1904 | ||||
-rw-r--r-- | sal/rtl/unload.cxx | 34 | ||||
-rw-r--r-- | sal/rtl/uri.cxx | 759 | ||||
-rw-r--r-- | sal/rtl/ustrbuf.cxx | 115 | ||||
-rw-r--r-- | sal/rtl/ustring.cxx | 1324 | ||||
-rw-r--r-- | sal/rtl/uuid.cxx | 173 |
29 files changed, 13451 insertions, 0 deletions
diff --git a/sal/rtl/alloc_arena.cxx b/sal/rtl/alloc_arena.cxx new file mode 100644 index 000000000..f126efdab --- /dev/null +++ b/sal/rtl/alloc_arena.cxx @@ -0,0 +1,1112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include "alloc_arena.hxx" + +#include "alloc_impl.hxx" +#include <rtllifecycle.h> +#include <sal/macros.h> + +#include <cassert> +#include <string.h> +#include <stdio.h> + +namespace { + +/** + @internal +*/ +struct rtl_arena_list_st +{ + rtl_memory_lock_type m_lock; + rtl_arena_type m_arena_head; +}; + +} + +static rtl_arena_list_st g_arena_list; + +/** + provided for arena_type allocations, and hash_table resizing. + + @internal +*/ +static rtl_arena_type * gp_arena_arena = nullptr; + +/** + Low level virtual memory (pseudo) arena + (platform dependent implementation) + + @internal + */ +static rtl_arena_type * gp_machdep_arena = nullptr; + +rtl_arena_type * gp_default_arena = nullptr; + +namespace +{ + +void * rtl_machdep_alloc( + rtl_arena_type * pArena, + sal_Size * pSize +); + +void rtl_machdep_free( + rtl_arena_type * pArena, + void * pAddr, + sal_Size nSize +); + +sal_Size rtl_machdep_pagesize(); + +void rtl_arena_segment_constructor(void * obj) +{ + rtl_arena_segment_type * segment = static_cast<rtl_arena_segment_type*>(obj); + + QUEUE_START_NAMED(segment, s); + QUEUE_START_NAMED(segment, f); +} + +void rtl_arena_segment_destructor(void * obj) +{ + rtl_arena_segment_type * segment = static_cast< rtl_arena_segment_type * >( + obj); + assert(QUEUE_STARTED_NAMED(segment, s)); + assert(QUEUE_STARTED_NAMED(segment, f)); + (void) segment; // avoid warnings +} + +/** + @precond arena->m_lock acquired. + */ +bool rtl_arena_segment_populate(rtl_arena_type * arena) +{ + rtl_arena_segment_type *span; + sal_Size size = rtl_machdep_pagesize(); + + span = static_cast< rtl_arena_segment_type * >( + rtl_machdep_alloc(gp_machdep_arena, &size)); + if (span) + { + rtl_arena_segment_type *first, *last, *head; + sal_Size count = size / sizeof(rtl_arena_segment_type); + + /* insert onto reserve span list */ + QUEUE_INSERT_TAIL_NAMED(&(arena->m_segment_reserve_span_head), span, s); + QUEUE_START_NAMED(span, f); + span->m_addr = reinterpret_cast<sal_uIntPtr>(span); + span->m_size = size; + span->m_type = RTL_ARENA_SEGMENT_TYPE_SPAN; + + /* insert onto reserve list */ + head = &(arena->m_segment_reserve_head); + for (first = span + 1, last = span + count; first < last; ++first) + { + QUEUE_INSERT_TAIL_NAMED(head, first, s); + QUEUE_START_NAMED(first, f); + first->m_addr = 0; + first->m_size = 0; + first->m_type = 0; + } + } + return (span != nullptr); +} + +/** + @precond arena->m_lock acquired. + @precond (*ppSegment == 0) +*/ +void rtl_arena_segment_get( + rtl_arena_type * arena, + rtl_arena_segment_type ** ppSegment +) +{ + rtl_arena_segment_type * head; + + assert(!*ppSegment); + + head = &(arena->m_segment_reserve_head); + if (head->m_snext != head || rtl_arena_segment_populate (arena)) + { + (*ppSegment) = head->m_snext; + QUEUE_REMOVE_NAMED(*ppSegment, s); + } +} + +/** + @precond arena->m_lock acquired. + @postcond (*ppSegment == 0) + */ +void rtl_arena_segment_put( + rtl_arena_type * arena, + rtl_arena_segment_type ** ppSegment +) +{ + rtl_arena_segment_type * head; + + assert(QUEUE_STARTED_NAMED(*ppSegment, s)); + assert(QUEUE_STARTED_NAMED(*ppSegment, f)); + + (*ppSegment)->m_addr = 0; + (*ppSegment)->m_size = 0; + + assert((*ppSegment)->m_type != RTL_ARENA_SEGMENT_TYPE_HEAD); + (*ppSegment)->m_type = 0; + + /* keep as reserve */ + head = &(arena->m_segment_reserve_head); + QUEUE_INSERT_HEAD_NAMED(head, (*ppSegment), s); + + /* clear */ + (*ppSegment) = nullptr; +} + +/** + @precond arena->m_lock acquired. +*/ +void rtl_arena_freelist_insert ( + rtl_arena_type * arena, + rtl_arena_segment_type * segment +) +{ + rtl_arena_segment_type * head; + const auto bit = highbit(segment->m_size); + assert(bit > 0); + head = &(arena->m_freelist_head[bit - 1]); + QUEUE_INSERT_TAIL_NAMED(head, segment, f); + + arena->m_freelist_bitmap |= head->m_size; +} + +/** + @precond arena->m_lock acquired. +*/ +void rtl_arena_freelist_remove( + rtl_arena_type * arena, + rtl_arena_segment_type * segment +) +{ + if (segment->m_fnext->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD && + segment->m_fprev->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD) + { + rtl_arena_segment_type * head; + + head = segment->m_fprev; + assert(arena->m_freelist_bitmap & head->m_size); + arena->m_freelist_bitmap ^= head->m_size; + } + QUEUE_REMOVE_NAMED(segment, f); +} + +#define RTL_ARENA_HASH_INDEX_IMPL(a, s, q, m) \ + ((((a) + ((a) >> (s)) + ((a) >> ((s) << 1))) >> (q)) & (m)) + +#define RTL_ARENA_HASH_INDEX(arena, addr) \ + RTL_ARENA_HASH_INDEX_IMPL((addr), (arena)->m_hash_shift, (arena)->m_quantum_shift, ((arena)->m_hash_size - 1)) + +/** + @precond arena->m_lock released. +*/ +void rtl_arena_hash_rescale( + rtl_arena_type * arena, + sal_Size new_size +) +{ + assert(new_size != 0); + + rtl_arena_segment_type ** new_table; + sal_Size new_bytes; + + new_bytes = new_size * sizeof(rtl_arena_segment_type*); + new_table = static_cast<rtl_arena_segment_type **>(rtl_arena_alloc (gp_arena_arena, &new_bytes)); + + if (new_table) + { + rtl_arena_segment_type ** old_table; + sal_Size old_size, i; + + memset (new_table, 0, new_bytes); + + RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock)); + + old_table = arena->m_hash_table; + old_size = arena->m_hash_size; + + arena->m_hash_table = new_table; + arena->m_hash_size = new_size; + arena->m_hash_shift = highbit(arena->m_hash_size) - 1; + + for (i = 0; i < old_size; i++) + { + rtl_arena_segment_type * curr = old_table[i]; + while (curr) + { + rtl_arena_segment_type * next = curr->m_fnext; + rtl_arena_segment_type ** head; + + // coverity[negative_shift] - bogus + head = &(arena->m_hash_table[RTL_ARENA_HASH_INDEX(arena, curr->m_addr)]); + curr->m_fnext = (*head); + (*head) = curr; + + curr = next; + } + old_table[i] = nullptr; + } + + RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock)); + + if (old_table != arena->m_hash_table_0) + { + sal_Size old_bytes = old_size * sizeof(rtl_arena_segment_type*); + rtl_arena_free (gp_arena_arena, old_table, old_bytes); + } + } +} + +/** + Insert arena hash, and update stats. +*/ +void rtl_arena_hash_insert( + rtl_arena_type * arena, + rtl_arena_segment_type * segment +) +{ + rtl_arena_segment_type ** ppSegment; + + ppSegment = &(arena->m_hash_table[RTL_ARENA_HASH_INDEX(arena, segment->m_addr)]); + + segment->m_fnext = (*ppSegment); + (*ppSegment) = segment; + + arena->m_stats.m_alloc += 1; + arena->m_stats.m_mem_alloc += segment->m_size; +} + +/** + Remove arena hash, and update stats. +*/ +rtl_arena_segment_type * rtl_arena_hash_remove( + rtl_arena_type * arena, + sal_uIntPtr addr, + sal_Size size +) +{ + rtl_arena_segment_type *segment, **segpp; + sal_Size lookups = 0; + + segpp = &(arena->m_hash_table[RTL_ARENA_HASH_INDEX(arena, addr)]); + while ((segment = *segpp)) + { + if (segment->m_addr == addr) + { + *segpp = segment->m_fnext; + segment->m_fnext = segment->m_fprev = segment; + break; + } + + /* update lookup miss stats */ + lookups += 1; + segpp = &(segment->m_fnext); + } + + assert(segment); // bad free + if (segment) + { + assert(segment->m_size == size); + (void) size; // avoid warnings + + arena->m_stats.m_free += 1; + arena->m_stats.m_mem_alloc -= segment->m_size; + + if (lookups > 1) + { + sal_Size nseg = static_cast<sal_Size>(arena->m_stats.m_alloc - arena->m_stats.m_free); + if (nseg > 4 * arena->m_hash_size) + { + if (!(arena->m_flags & RTL_ARENA_FLAG_RESCALE)) + { + sal_Size ave = nseg >> arena->m_hash_shift; + assert(ave != 0); + sal_Size new_size = arena->m_hash_size << (highbit(ave) - 1); + + arena->m_flags |= RTL_ARENA_FLAG_RESCALE; + RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock)); + rtl_arena_hash_rescale (arena, new_size); + RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock)); + arena->m_flags &= ~RTL_ARENA_FLAG_RESCALE; + } + } + } + } + + return segment; +} + +/** + allocate (and remove) segment from freelist + + @precond arena->m_lock acquired + @precond (*ppSegment == 0) +*/ +bool rtl_arena_segment_alloc( + rtl_arena_type * arena, + sal_Size size, + rtl_arena_segment_type ** ppSegment +) +{ + int index = 0; + + assert(!*ppSegment); + if (!RTL_MEMORY_ISP2(size)) + { + unsigned int msb = highbit(size); + if (RTL_ARENA_FREELIST_SIZE == msb) + { + /* highest possible freelist: fall back to first fit */ + rtl_arena_segment_type *head, *segment; + + head = &(arena->m_freelist_head[msb - 1]); + for (segment = head->m_fnext; segment != head; segment = segment->m_fnext) + { + if (segment->m_size >= size) + { + /* allocate first fit segment */ + (*ppSegment) = segment; + break; + } + } + goto dequeue_and_leave; + } + + /* roundup to next power of 2 */ + size = ((sal_Size(1)) << msb); + } + + index = lowbit(RTL_MEMORY_P2ALIGN(arena->m_freelist_bitmap, size)); + if (index > 0) + { + /* instant fit: allocate first free segment */ + rtl_arena_segment_type *head; + + head = &(arena->m_freelist_head[index - 1]); + (*ppSegment) = head->m_fnext; + assert((*ppSegment) != head); + } + +dequeue_and_leave: + if (*ppSegment) + { + /* remove from freelist */ + rtl_arena_freelist_remove (arena, (*ppSegment)); + } + return (*ppSegment != nullptr); +} + +/** + import new (span) segment from source arena + + @precond arena->m_lock acquired + @precond (*ppSegment == 0) +*/ +bool rtl_arena_segment_create( + rtl_arena_type * arena, + sal_Size size, + rtl_arena_segment_type ** ppSegment +) +{ + assert(!*ppSegment); + if (arena->m_source_alloc) + { + rtl_arena_segment_get (arena, ppSegment); + if (*ppSegment) + { + rtl_arena_segment_type * span = nullptr; + rtl_arena_segment_get (arena, &span); + if (span) + { + /* import new span from source arena */ + RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock)); + + span->m_size = size; + span->m_addr = reinterpret_cast<sal_uIntPtr>( + (arena->m_source_alloc)( + arena->m_source_arena, &(span->m_size))); + + RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock)); + if (span->m_addr != 0) + { + /* insert onto segment list, update stats */ + span->m_type = RTL_ARENA_SEGMENT_TYPE_SPAN; + QUEUE_INSERT_HEAD_NAMED(&(arena->m_segment_head), span, s); + arena->m_stats.m_mem_total += span->m_size; + + (*ppSegment)->m_addr = span->m_addr; + (*ppSegment)->m_size = span->m_size; + (*ppSegment)->m_type = RTL_ARENA_SEGMENT_TYPE_FREE; + QUEUE_INSERT_HEAD_NAMED(span, (*ppSegment), s); + + /* report success */ + return true; + } + rtl_arena_segment_put (arena, &span); + } + rtl_arena_segment_put (arena, ppSegment); + } + } + return false; // failure +} + +/** + mark as free and join with adjacent free segment(s) + + @precond arena->m_lock acquired + @precond segment marked 'used' +*/ +void rtl_arena_segment_coalesce( + rtl_arena_type * arena, + rtl_arena_segment_type * segment +) +{ + rtl_arena_segment_type *next, *prev; + + /* mark segment free */ + assert(segment->m_type == RTL_ARENA_SEGMENT_TYPE_USED); + segment->m_type = RTL_ARENA_SEGMENT_TYPE_FREE; + + /* try to merge w/ next segment */ + next = segment->m_snext; + if (next->m_type == RTL_ARENA_SEGMENT_TYPE_FREE) + { + assert(segment->m_addr + segment->m_size == next->m_addr); + segment->m_size += next->m_size; + + /* remove from freelist */ + rtl_arena_freelist_remove (arena, next); + + /* remove from segment list */ + QUEUE_REMOVE_NAMED(next, s); + + /* release segment descriptor */ + rtl_arena_segment_put (arena, &next); + } + + /* try to merge w/ prev segment */ + prev = segment->m_sprev; + if (prev->m_type == RTL_ARENA_SEGMENT_TYPE_FREE) + { + assert(prev->m_addr + prev->m_size == segment->m_addr); + segment->m_addr = prev->m_addr; + segment->m_size += prev->m_size; + + /* remove from freelist */ + rtl_arena_freelist_remove (arena, prev); + + /* remove from segment list */ + QUEUE_REMOVE_NAMED(prev, s); + + /* release segment descriptor */ + rtl_arena_segment_put (arena, &prev); + } +} + +void rtl_arena_constructor(void * obj) +{ + rtl_arena_type * arena = static_cast<rtl_arena_type*>(obj); + rtl_arena_segment_type * head; + size_t i; + + memset (arena, 0, sizeof(rtl_arena_type)); + + QUEUE_START_NAMED(arena, arena_); + + RTL_MEMORY_LOCK_INIT(&(arena->m_lock)); + + head = &(arena->m_segment_reserve_span_head); + rtl_arena_segment_constructor (head); + head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD; + + head = &(arena->m_segment_reserve_head); + rtl_arena_segment_constructor (head); + head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD; + + head = &(arena->m_segment_head); + rtl_arena_segment_constructor (head); + head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD; + + for (i = 0; i < RTL_ARENA_FREELIST_SIZE; i++) + { + head = &(arena->m_freelist_head[i]); + rtl_arena_segment_constructor (head); + + head->m_size = ((sal_Size(1)) << i); + head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD; + } + + arena->m_hash_table = arena->m_hash_table_0; + arena->m_hash_size = RTL_ARENA_HASH_SIZE; + arena->m_hash_shift = highbit(arena->m_hash_size) - 1; +} + +void rtl_arena_destructor(void * obj) +{ + rtl_arena_type * arena = static_cast<rtl_arena_type*>(obj); + rtl_arena_segment_type * head; + size_t i; + + assert(QUEUE_STARTED_NAMED(arena, arena_)); + + RTL_MEMORY_LOCK_DESTROY(&(arena->m_lock)); + + head = &(arena->m_segment_reserve_span_head); + assert(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD); + rtl_arena_segment_destructor (head); + + head = &(arena->m_segment_reserve_head); + assert(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD); + rtl_arena_segment_destructor (head); + + head = &(arena->m_segment_head); + assert(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD); + rtl_arena_segment_destructor (head); + + for (i = 0; i < RTL_ARENA_FREELIST_SIZE; i++) + { + head = &(arena->m_freelist_head[i]); + + assert(head->m_size == ((sal_Size(1)) << i)); + assert(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD); + + rtl_arena_segment_destructor (head); + } + + assert(arena->m_hash_table == arena->m_hash_table_0); + assert(arena->m_hash_size == RTL_ARENA_HASH_SIZE); + assert(arena->m_hash_shift == highbit(arena->m_hash_size) - 1); +} + +rtl_arena_type * rtl_arena_activate( + rtl_arena_type * arena, + const char * name, + sal_Size quantum, + rtl_arena_type * source_arena, + void * (SAL_CALL * source_alloc)(rtl_arena_type *, sal_Size *), + void (SAL_CALL * source_free) (rtl_arena_type *, void *, sal_Size) +) +{ + assert(arena); + if (arena) + { + (void) snprintf (arena->m_name, sizeof(arena->m_name), "%s", name); + + if (!RTL_MEMORY_ISP2(quantum)) + { + /* roundup to next power of 2 */ + quantum = ((sal_Size(1)) << highbit(quantum)); + } + + arena->m_quantum = quantum; + arena->m_quantum_shift = highbit(arena->m_quantum) - 1; + + arena->m_source_arena = source_arena; + arena->m_source_alloc = source_alloc; + arena->m_source_free = source_free; + + /* insert into arena list */ + RTL_MEMORY_LOCK_ACQUIRE(&(g_arena_list.m_lock)); + QUEUE_INSERT_TAIL_NAMED(&(g_arena_list.m_arena_head), arena, arena_); + RTL_MEMORY_LOCK_RELEASE(&(g_arena_list.m_lock)); + } + return arena; +} + +void rtl_arena_deactivate(rtl_arena_type * arena) +{ + rtl_arena_segment_type * head, * segment; + + /* remove from arena list */ + RTL_MEMORY_LOCK_ACQUIRE(&(g_arena_list.m_lock)); + QUEUE_REMOVE_NAMED(arena, arena_); + RTL_MEMORY_LOCK_RELEASE(&(g_arena_list.m_lock)); + + /* check for leaked segments */ + if (arena->m_stats.m_alloc > arena->m_stats.m_free) + { + sal_Size i, n; + + /* cleanup still used segment(s) */ + for (i = 0, n = arena->m_hash_size; i < n; i++) + { + while ((segment = arena->m_hash_table[i])) + { + /* pop from hash table */ + arena->m_hash_table[i] = segment->m_fnext; + segment->m_fnext = segment->m_fprev = segment; + + /* coalesce w/ adjacent free segment(s) */ + rtl_arena_segment_coalesce (arena, segment); + + /* insert onto freelist */ + rtl_arena_freelist_insert (arena, segment); + } + } + } + + /* cleanup hash table */ + if (arena->m_hash_table != arena->m_hash_table_0) + { + rtl_arena_free( + gp_arena_arena, + arena->m_hash_table, + arena->m_hash_size * sizeof(rtl_arena_segment_type*)); + + arena->m_hash_table = arena->m_hash_table_0; + arena->m_hash_size = RTL_ARENA_HASH_SIZE; + arena->m_hash_shift = highbit(arena->m_hash_size) - 1; + } + + /* cleanup segment list */ + head = &(arena->m_segment_head); + for (segment = head->m_snext; segment != head; segment = head->m_snext) + { + if (segment->m_type == RTL_ARENA_SEGMENT_TYPE_FREE) + { + /* remove from freelist */ + rtl_arena_freelist_remove (arena, segment); + } + else + { + /* can have only free and span segments here */ + assert(segment->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN); + } + + /* remove from segment list */ + QUEUE_REMOVE_NAMED(segment, s); + + /* release segment descriptor */ + rtl_arena_segment_put (arena, &segment); + } + + /* cleanup segment reserve list */ + head = &(arena->m_segment_reserve_head); + for (segment = head->m_snext; segment != head; segment = head->m_snext) + { + /* remove from segment list */ + QUEUE_REMOVE_NAMED(segment, s); + } + + /* cleanup segment reserve span(s) */ + head = &(arena->m_segment_reserve_span_head); + for (segment = head->m_snext; segment != head; segment = head->m_snext) + { + /* can have only span segments here */ + assert(segment->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN); + + /* remove from segment list */ + QUEUE_REMOVE_NAMED(segment, s); + + /* return span to g_machdep_arena */ + rtl_machdep_free (gp_machdep_arena, reinterpret_cast<void*>(segment->m_addr), segment->m_size); + } +} + +} // namespace + +rtl_arena_type * SAL_CALL rtl_arena_create( + const char * name, + sal_Size quantum, + sal_Size, + rtl_arena_type * source_arena, + void * (SAL_CALL * source_alloc)(rtl_arena_type *, sal_Size *), + void (SAL_CALL * source_free) (rtl_arena_type *, void *, sal_Size), + SAL_UNUSED_PARAMETER int +) SAL_THROW_EXTERN_C() +{ + rtl_arena_type * result = nullptr; + sal_Size size = sizeof(rtl_arena_type); + +try_alloc: + result = static_cast<rtl_arena_type*>(rtl_arena_alloc (gp_arena_arena, &size)); + if (result) + { + rtl_arena_type * arena = result; + rtl_arena_constructor (arena); + + if (!source_arena) + { + assert(gp_default_arena); + source_arena = gp_default_arena; + } + + result = rtl_arena_activate ( + arena, + name, + quantum, + source_arena, + source_alloc, + source_free + ); + + if (!result) + { + rtl_arena_deactivate (arena); + rtl_arena_destructor (arena); + rtl_arena_free (gp_arena_arena, arena, size); + } + } + else if (!gp_arena_arena) + { + ensureArenaSingleton(); + if (gp_arena_arena) + { + /* try again */ + goto try_alloc; + } + } + return result; +} + +void SAL_CALL rtl_arena_destroy(rtl_arena_type * arena) SAL_THROW_EXTERN_C() +{ + if (arena) + { + rtl_arena_deactivate (arena); + rtl_arena_destructor (arena); + rtl_arena_free (gp_arena_arena, arena, sizeof(rtl_arena_type)); + } +} + +void * SAL_CALL rtl_arena_alloc( + rtl_arena_type * arena, + sal_Size * pSize +) SAL_THROW_EXTERN_C() +{ + void * addr = nullptr; + + if (arena && pSize) + { + sal_Size size; + + size = RTL_MEMORY_ALIGN(*pSize, arena->m_quantum); + if (size > 0) + { + /* allocate from segment list */ + rtl_arena_segment_type *segment = nullptr; + + RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock)); + if (rtl_arena_segment_alloc (arena, size, &segment) || + rtl_arena_segment_create(arena, size, &segment) ) + { + /* shrink to fit */ + sal_Size oversize; + + /* mark segment used */ + assert(segment->m_type == RTL_ARENA_SEGMENT_TYPE_FREE); + segment->m_type = RTL_ARENA_SEGMENT_TYPE_USED; + + /* resize */ + assert(segment->m_size >= size); + oversize = segment->m_size - size; + if (oversize >= arena->m_quantum) + { + rtl_arena_segment_type * remainder = nullptr; + rtl_arena_segment_get (arena, &remainder); + if (remainder) + { + segment->m_size = size; + + remainder->m_addr = segment->m_addr + segment->m_size; + remainder->m_size = oversize; + remainder->m_type = RTL_ARENA_SEGMENT_TYPE_FREE; + QUEUE_INSERT_HEAD_NAMED(segment, remainder, s); + + rtl_arena_freelist_insert (arena, remainder); + } + } + + rtl_arena_hash_insert (arena, segment); + + (*pSize) = segment->m_size; + addr = reinterpret_cast<void*>(segment->m_addr); + } + RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock)); + } + } + return addr; +} + +void SAL_CALL rtl_arena_free ( + rtl_arena_type * arena, + void * addr, + sal_Size size +) SAL_THROW_EXTERN_C() +{ + if (arena) + { + size = RTL_MEMORY_ALIGN(size, arena->m_quantum); + if (size > 0) + { + /* free to segment list */ + rtl_arena_segment_type * segment; + + RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock)); + + segment = rtl_arena_hash_remove (arena, reinterpret_cast<sal_uIntPtr>(addr), size); + if (segment) + { + rtl_arena_segment_type *next, *prev; + + /* coalesce w/ adjacent free segment(s) */ + rtl_arena_segment_coalesce (arena, segment); + + /* determine (new) next and prev segment */ + next = segment->m_snext; + prev = segment->m_sprev; + + /* entire span free when prev is a span, and next is either a span or a list head */ + if (prev->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN && + ((next->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN) || + (next->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD))) + { + assert( + prev->m_addr == segment->m_addr + && prev->m_size == segment->m_size); + + if (arena->m_source_free) + { + addr = reinterpret_cast<void*>(prev->m_addr); + size = prev->m_size; + + /* remove from segment list */ + QUEUE_REMOVE_NAMED(segment, s); + + /* release segment descriptor */ + rtl_arena_segment_put (arena, &segment); + + /* remove from segment list */ + QUEUE_REMOVE_NAMED(prev, s); + + /* release (span) segment descriptor */ + rtl_arena_segment_put (arena, &prev); + + /* update stats, return span to source arena */ + arena->m_stats.m_mem_total -= size; + RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock)); + + (arena->m_source_free)(arena->m_source_arena, addr, size); + return; + } + } + + /* insert onto freelist */ + rtl_arena_freelist_insert (arena, segment); + } + + RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock)); + } + } +} + +void rtl_arena_foreach (rtl_arena_type *arena, ArenaForeachFn foreachFn) +{ + /* used segments */ + for (int i = 0, n = arena->m_hash_size; i < n; i++) + { + for (rtl_arena_segment_type *segment = arena->m_hash_table[i]; + segment != nullptr; segment = segment->m_fnext) + { + foreachFn(reinterpret_cast<void *>(segment->m_addr), + segment->m_size); + } + } +} + +#if defined(SAL_UNX) +#include <sys/mman.h> +#elif defined(_WIN32) +#define MAP_FAILED nullptr +#endif /* SAL_UNX || _WIN32 */ + +namespace +{ + +void * rtl_machdep_alloc( + rtl_arena_type * pArena, + sal_Size * pSize +) +{ + void * addr; + sal_Size size = *pSize; + + assert(pArena == gp_machdep_arena); + +#if defined(__sun) && defined(SPARC) + /* see @ mmap(2) man pages */ + size += (pArena->m_quantum + pArena->m_quantum); /* "red-zone" pages */ + if (size > (4 << 20)) + size = RTL_MEMORY_P2ROUNDUP(size, (4 << 20)); + else if (size > (512 << 10)) + size = RTL_MEMORY_P2ROUNDUP(size, (512 << 10)); + else + size = RTL_MEMORY_P2ROUNDUP(size, (64 << 10)); + size -= (pArena->m_quantum + pArena->m_quantum); /* "red-zone" pages */ +#else + /* default allocation granularity */ + if (pArena->m_quantum < (64 << 10)) + { + size = RTL_MEMORY_P2ROUNDUP(size, (64 << 10)); + } + else + { + size = RTL_MEMORY_P2ROUNDUP(size, pArena->m_quantum); + } +#endif + +#if defined(SAL_UNX) + addr = mmap (nullptr, static_cast<size_t>(size), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); +#elif defined(_WIN32) + addr = VirtualAlloc (nullptr, static_cast<SIZE_T>(size), MEM_COMMIT, PAGE_READWRITE); +#endif /* (SAL_UNX || _WIN32) */ + + if (addr != MAP_FAILED) + { + pArena->m_stats.m_alloc += 1; + pArena->m_stats.m_mem_total += size; + pArena->m_stats.m_mem_alloc += size; + + (*pSize) = size; + return addr; + } + return nullptr; +} + +void rtl_machdep_free( + rtl_arena_type * pArena, + void * pAddr, + sal_Size nSize +) +{ + assert(pArena == gp_machdep_arena); + + pArena->m_stats.m_free += 1; + pArena->m_stats.m_mem_total -= nSize; + pArena->m_stats.m_mem_alloc -= nSize; + +#if defined(SAL_UNX) + (void) munmap(pAddr, nSize); +#elif defined(_WIN32) + (void) VirtualFree (pAddr, SIZE_T(0), MEM_RELEASE); +#endif /* (SAL_UNX || _WIN32) */ +} + +sal_Size rtl_machdep_pagesize() +{ +#if defined(SAL_UNX) +#if defined(FREEBSD) || defined(NETBSD) || defined(DRAGONFLY) + return (sal_Size)getpagesize(); +#else /* POSIX */ + return static_cast<sal_Size>(sysconf(_SC_PAGESIZE)); +#endif /* xBSD || POSIX */ +#elif defined(_WIN32) + SYSTEM_INFO info; + GetSystemInfo (&info); + return static_cast<sal_Size>(info.dwPageSize); +#endif /* (SAL_UNX || _WIN32) */ +} + +} //namespace + +void rtl_arena_init() +{ + { + /* list of arenas */ + RTL_MEMORY_LOCK_INIT(&(g_arena_list.m_lock)); + rtl_arena_constructor (&(g_arena_list.m_arena_head)); + } + { + /* machdep (pseudo) arena */ + static rtl_arena_type g_machdep_arena; + + assert(!gp_machdep_arena); + rtl_arena_constructor (&g_machdep_arena); + + gp_machdep_arena = rtl_arena_activate ( + &g_machdep_arena, + "rtl_machdep_arena", + rtl_machdep_pagesize(), + nullptr, nullptr, nullptr /* no source */ + ); + assert(gp_machdep_arena); + } + { + /* default arena */ + static rtl_arena_type g_default_arena; + + assert(!gp_default_arena); + rtl_arena_constructor (&g_default_arena); + + gp_default_arena = rtl_arena_activate ( + &g_default_arena, + "rtl_default_arena", + rtl_machdep_pagesize(), + gp_machdep_arena, /* source */ + rtl_machdep_alloc, + rtl_machdep_free + ); + assert(gp_default_arena); + } + { + /* arena internal arena */ + static rtl_arena_type g_arena_arena; + + assert(!gp_arena_arena); + rtl_arena_constructor (&g_arena_arena); + + gp_arena_arena = rtl_arena_activate( + &g_arena_arena, + "rtl_arena_internal_arena", + 64, /* quantum */ + gp_default_arena, /* source */ + rtl_arena_alloc, + rtl_arena_free + ); + assert(gp_arena_arena); + } +} + +void rtl_arena_fini() +{ + if (gp_arena_arena) + { + rtl_arena_type * arena, * head; + + RTL_MEMORY_LOCK_ACQUIRE(&(g_arena_list.m_lock)); + head = &(g_arena_list.m_arena_head); + + for (arena = head->m_arena_next; arena != head; arena = arena->m_arena_next) + { + // noop + } + RTL_MEMORY_LOCK_RELEASE(&(g_arena_list.m_lock)); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/alloc_arena.hxx b/sal/rtl/alloc_arena.hxx new file mode 100644 index 000000000..7226ef3f1 --- /dev/null +++ b/sal/rtl/alloc_arena.hxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SAL_RTL_ALLOC_ARENA_HXX +#define INCLUDED_SAL_RTL_ALLOC_ARENA_HXX + +#include <sal/types.h> +#include <rtl/alloc.h> +#include "alloc_impl.hxx" + +/** rtl_arena_stat_type + * @internal + */ +struct rtl_arena_stat_type +{ + sal_uInt64 m_alloc; + sal_uInt64 m_free; + + sal_Size m_mem_total; + sal_Size m_mem_alloc; +}; + +/** rtl_arena_segment_type + * @internal + */ +constexpr sal_Size RTL_ARENA_SEGMENT_TYPE_HEAD = 0x01; +constexpr sal_Size RTL_ARENA_SEGMENT_TYPE_SPAN = 0x02; +constexpr sal_Size RTL_ARENA_SEGMENT_TYPE_FREE = 0x04; +constexpr sal_Size RTL_ARENA_SEGMENT_TYPE_USED = 0x08; + +struct rtl_arena_segment_type +{ + /* segment list linkage */ + rtl_arena_segment_type * m_snext; + rtl_arena_segment_type * m_sprev; + + /* free/used list linkage */ + rtl_arena_segment_type * m_fnext; + rtl_arena_segment_type * m_fprev; + + /* segment description */ + sal_uIntPtr m_addr; + sal_Size m_size; + sal_Size m_type; +}; + +/** rtl_arena_type + * @internal + */ +constexpr auto RTL_ARENA_FREELIST_SIZE = sizeof(void*) * 8; +constexpr auto RTL_ARENA_HASH_SIZE = 64; + +constexpr auto RTL_ARENA_FLAG_RESCALE = 1; /* within hash rescale operation */ + +struct rtl_arena_st +{ + /* linkage */ + rtl_arena_type * m_arena_next; + rtl_arena_type * m_arena_prev; + + /* properties */ + char m_name[RTL_ARENA_NAME_LENGTH + 1]; + long m_flags; + + rtl_memory_lock_type m_lock; + rtl_arena_stat_type m_stats; + + rtl_arena_type * m_source_arena; + void * (SAL_CALL * m_source_alloc)(rtl_arena_type *, sal_Size *); + void (SAL_CALL * m_source_free) (rtl_arena_type *, void *, sal_Size); + + sal_Size m_quantum; + sal_Size m_quantum_shift; /* log2(m_quantum) */ + + rtl_arena_segment_type m_segment_reserve_span_head; + rtl_arena_segment_type m_segment_reserve_head; + + rtl_arena_segment_type m_segment_head; + + rtl_arena_segment_type m_freelist_head[RTL_ARENA_FREELIST_SIZE]; + sal_Size m_freelist_bitmap; + + rtl_arena_segment_type ** m_hash_table; + rtl_arena_segment_type * m_hash_table_0[RTL_ARENA_HASH_SIZE]; + sal_Size m_hash_size; /* m_hash_mask + 1 */ + sal_Size m_hash_shift; /* log2(m_hash_size) */ +}; + +/** gp_default_arena + * default arena with pagesize quantum + * + * @internal + */ +extern rtl_arena_type * gp_default_arena; + +typedef void (*ArenaForeachFn)(void *addr, sal_Size size); + +void rtl_arena_foreach(rtl_arena_type *arena, ArenaForeachFn fn); + +#endif // INCLUDED_SAL_RTL_ALLOC_ARENA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/alloc_cache.cxx b/sal/rtl/alloc_cache.cxx new file mode 100644 index 000000000..1f165cca1 --- /dev/null +++ b/sal/rtl/alloc_cache.cxx @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "alloc_cache.hxx" +#include "alloc_impl.hxx" +#include "alloc_arena.hxx" +#include <rtllifecycle.h> + +#include <cassert> +#include <cstdlib> +#include <string.h> +#include <stdio.h> + +/** + provided for cache_type allocations, and hash_table resizing. + + @internal +*/ +static rtl_arena_type * gp_cache_arena = nullptr; + +namespace +{ + +rtl_cache_type * rtl_cache_activate( + rtl_cache_type * cache, + const char * name, + size_t objsize, + size_t objalign, + int (SAL_CALL * constructor)(void * obj, void * userarg), + void (SAL_CALL * destructor) (void * obj, void * userarg), + void * userarg +) +{ + assert(cache); + + snprintf (cache->m_name, sizeof(cache->m_name), "%s", name); + + if (objalign == 0) + { + /* determine default alignment */ + if (objsize >= RTL_MEMORY_ALIGNMENT_8) + objalign = RTL_MEMORY_ALIGNMENT_8; + else + objalign = RTL_MEMORY_ALIGNMENT_4; + } + else + { + /* ensure minimum alignment */ + if(objalign < RTL_MEMORY_ALIGNMENT_4) + { + objalign = RTL_MEMORY_ALIGNMENT_4; + } + } + assert(RTL_MEMORY_ISP2(objalign)); + + cache->m_type_size = RTL_MEMORY_P2ROUNDUP(objsize, objalign); + + cache->m_constructor = constructor; + cache->m_destructor = destructor; + cache->m_userarg = userarg; + + return cache; +} + +} //namespace + +rtl_cache_type * SAL_CALL rtl_cache_create( + const char * name, + sal_Size objsize, + sal_Size objalign, + int (SAL_CALL * constructor)(void * obj, void * userarg), + void (SAL_CALL * destructor) (void * obj, void * userarg), + void (SAL_CALL * /*reclaim*/) (void * userarg), + void * userarg, + rtl_arena_type *, + int +) SAL_THROW_EXTERN_C() +{ + rtl_cache_type * result = nullptr; + sal_Size size = sizeof(rtl_cache_type); + +try_alloc: + result = static_cast<rtl_cache_type*>(rtl_arena_alloc (gp_cache_arena, &size)); + if (result) + { + rtl_cache_type * cache = result; + memset (cache, 0, sizeof(rtl_cache_type)); + + result = rtl_cache_activate ( + cache, + name, + objsize, + objalign, + constructor, + destructor, + userarg + ); + + if (!result) + { + /* activation failed */ + rtl_arena_free (gp_cache_arena, cache, size); + } + } + else if (!gp_cache_arena) + { + ensureCacheSingleton(); + if (gp_cache_arena) + { + /* try again */ + goto try_alloc; + } + } + return result; +} + +void SAL_CALL rtl_cache_destroy(rtl_cache_type * cache) SAL_THROW_EXTERN_C() +{ + if (cache) + { + rtl_arena_free (gp_cache_arena, cache, sizeof(rtl_cache_type)); + } +} + +void * SAL_CALL rtl_cache_alloc(rtl_cache_type * cache) SAL_THROW_EXTERN_C() +{ + void * obj = nullptr; + + if (!cache) + return nullptr; + + obj = std::malloc(cache->m_type_size); + if (obj && cache->m_constructor) + { + if (!(cache->m_constructor)(obj, cache->m_userarg)) + { + /* construction failure */ + std::free(obj); + obj = nullptr; + } + } + return obj; +} + +void SAL_CALL rtl_cache_free( + rtl_cache_type * cache, + void * obj +) SAL_THROW_EXTERN_C() +{ + if (obj && cache) + { + if (cache->m_destructor) + { + /* destruct object */ + (cache->m_destructor)(obj, cache->m_userarg); + } + std::free(obj); + } +} + +#if defined(SAL_UNX) + +void SAL_CALL rtl_secureZeroMemory(void *Ptr, sal_Size Bytes) SAL_THROW_EXTERN_C() +{ + //currently glibc doesn't implement memset_s + volatile char *p = static_cast<volatile char*>(Ptr); + while (Bytes--) + *p++ = 0; +} + +#elif defined(_WIN32) + +void SAL_CALL rtl_secureZeroMemory(void *Ptr, sal_Size Bytes) SAL_THROW_EXTERN_C() +{ + RtlSecureZeroMemory(Ptr, Bytes); +} + +#endif /* SAL_UNX || _WIN32 */ + +void rtl_cache_init() +{ + /* cache: internal arena */ + assert(!gp_cache_arena); + + gp_cache_arena = rtl_arena_create ( + "rtl_cache_internal_arena", + 64, /* quantum */ + 0, /* no quantum caching */ + nullptr, /* default source */ + rtl_arena_alloc, + rtl_arena_free, + 0 /* flags */ + ); + assert(gp_cache_arena); + + /* check 'gp_default_arena' initialization */ + assert(gp_default_arena); +} + +void rtl_cache_fini() +{ + if (gp_cache_arena) + { + rtl_arena_destroy (gp_cache_arena); + gp_cache_arena = nullptr; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/alloc_cache.hxx b/sal/rtl/alloc_cache.hxx new file mode 100644 index 000000000..501d5770b --- /dev/null +++ b/sal/rtl/alloc_cache.hxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SAL_RTL_ALLOC_CACHE_HXX +#define INCLUDED_SAL_RTL_ALLOC_CACHE_HXX + +#include <sal/types.h> +#include <rtl/alloc.h> + +struct rtl_cache_st +{ + /* properties */ + char m_name[RTL_CACHE_NAME_LENGTH + 1]; + + sal_Size m_type_size; /* const */ + + int (SAL_CALL * m_constructor)(void * obj, void * userarg); /* const */ + void (SAL_CALL * m_destructor) (void * obj, void * userarg); /* const */ + void * m_userarg; +}; + +#endif // INCLUDED_SAL_RTL_ALLOC_CACHE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/alloc_fini.cxx b/sal/rtl/alloc_fini.cxx new file mode 100644 index 000000000..ba798452d --- /dev/null +++ b/sal/rtl/alloc_fini.cxx @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <rtllifecycle.h> + +namespace +{ + struct rtlCacheSingleton + { + rtlCacheSingleton() + { + rtl_cache_init(); + } + ~rtlCacheSingleton() + { + rtl_cache_fini(); + } + }; + rtlCacheSingleton& theCacheSingleton() + { + static rtlCacheSingleton SINGLETON; + return SINGLETON; + } +} + +void ensureCacheSingleton() +{ + theCacheSingleton(); +} + +namespace +{ + struct rtlArenaSingleton + { + rtlArenaSingleton() + { + rtl_arena_init(); + } + ~rtlArenaSingleton() + { + rtl_arena_fini(); + } + }; + rtlArenaSingleton& theArenaSingleton() + { + static rtlArenaSingleton SINGLETON; + return SINGLETON; + } +} + +void ensureArenaSingleton() +{ + theArenaSingleton(); +} + +namespace +{ + struct rtlLocaleSingleton + { + rtlLocaleSingleton() + { + rtl_locale_init(); + } + ~rtlLocaleSingleton() + { + rtl_locale_fini(); + } + }; + rtlLocaleSingleton& theLocaleSingleton() + { + static rtlLocaleSingleton SINGLETON; + return SINGLETON; + } +} + +void ensureLocaleSingleton() +{ + theLocaleSingleton(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/alloc_global.cxx b/sal/rtl/alloc_global.cxx new file mode 100644 index 000000000..4b7ec6037 --- /dev/null +++ b/sal/rtl/alloc_global.cxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "alloc_impl.hxx" +#include <rtl/alloc.h> +#include <sal/log.hxx> +#include <sal/macros.h> + +#include <algorithm> +#include <cassert> +#include <string.h> +#include <stdio.h> + +#include <rtllifecycle.h> +#include <oslmemory.h> + +void* SAL_CALL rtl_allocateMemory(sal_Size n) SAL_THROW_EXTERN_C() +{ + SAL_WARN_IF( + n >= SAL_MAX_INT32, "sal.rtl", + "suspicious massive alloc " << n); + return malloc (n); +} + +void* SAL_CALL rtl_reallocateMemory(void * p, sal_Size n) SAL_THROW_EXTERN_C() +{ + SAL_WARN_IF( + n >= SAL_MAX_INT32, "sal.rtl", + "suspicious massive alloc " << n); + return realloc (p, n); +} + +void SAL_CALL rtl_freeMemory(void * p) SAL_THROW_EXTERN_C() +{ + free (p); +} + +void * SAL_CALL rtl_allocateZeroMemory(sal_Size n) SAL_THROW_EXTERN_C() +{ + SAL_WARN_IF( n >= SAL_MAX_INT32, "sal.rtl", "suspicious massive alloc " << n); + return calloc(n, 1); +} + +void SAL_CALL rtl_freeZeroMemory(void * p, sal_Size n) SAL_THROW_EXTERN_C() +{ + if (p) + { + rtl_secureZeroMemory (p, n); + rtl_freeMemory (p); + } +} + +void* SAL_CALL rtl_allocateAlignedMemory(sal_Size Alignment, sal_Size Bytes) SAL_THROW_EXTERN_C() +{ + return osl_aligned_alloc(Alignment, Bytes); +} + +void SAL_CALL rtl_freeAlignedMemory(void* Ptr) SAL_THROW_EXTERN_C() +{ + osl_aligned_free(Ptr); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/alloc_impl.hxx b/sal/rtl/alloc_impl.hxx new file mode 100644 index 000000000..29c24cc99 --- /dev/null +++ b/sal/rtl/alloc_impl.hxx @@ -0,0 +1,219 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SAL_RTL_ALLOC_IMPL_HXX +#define INCLUDED_SAL_RTL_ALLOC_IMPL_HXX + +#include <sal/types.h> + +/** Alignment macros + */ +#if SAL_TYPES_ALIGNMENT4 > 1 +#define RTL_MEMORY_ALIGNMENT_4 SAL_TYPES_ALIGNMENT4 +#else +#define RTL_MEMORY_ALIGNMENT_4 sizeof(int) +#endif /* SAL_TYPES_ALIGNMENT4 */ + +#if SAL_TYPES_ALIGNMENT8 > 1 +#define RTL_MEMORY_ALIGNMENT_8 SAL_TYPES_ALIGNMENT8 +#else +#define RTL_MEMORY_ALIGNMENT_8 sizeof(void*) +#endif /* SAL_TYPES_ALIGNMENT8 */ + +#if 0 /* @@@ */ +#define RTL_MEMORY_ALIGNMENT_1 8 +#define RTL_MEMORY_ALIGNMENT_2 (sizeof(void*) * 2) +#endif /* @@@ */ + +#define RTL_MEMORY_ALIGN(value, align) (((value) + ((align) - 1)) & ~((align) - 1)) + +#define RTL_MEMORY_ISP2(value) (((value) & ((value) - 1)) == 0) +#define RTL_MEMORY_P2ALIGN(value, align) ((value) & -static_cast<sal_IntPtr>(align)) + +#define RTL_MEMORY_P2ROUNDUP(value, align) \ + (-(-static_cast<sal_IntPtr>(value) & -static_cast<sal_IntPtr>(align))) +#define RTL_MEMORY_P2END(value, align) \ + (-(~static_cast<sal_IntPtr>(value) & -static_cast<sal_IntPtr>(align))) + +/** highbit(): log2() + 1 + (complexity O(1)) +*/ +static inline unsigned int highbit(sal_Size n) +{ + unsigned int k = 1; + + if (n == 0) + return 0; + if constexpr (sizeof(n) >= 8) + { + if (n & 0xffffffff00000000) + { + k |= 32; + n >>= 32; + } + } + if (n & 0xffff0000) + { + k |= 16; + n >>= 16; + } + if (n & 0xff00) + { + k |= 8; + n >>= 8; + } + if (n & 0xf0) + { + k |= 4; + n >>= 4; + } + if (n & 0x0c) + { + k |= 2; + n >>= 2; + } + if (n & 0x02) + k++; + + return k; +} + +/** find first bit set + (complexity O(1)) +*/ +static inline unsigned int lowbit(sal_Size n) +{ + unsigned int k = 1; + + if (n == 0) + return 0; + + if constexpr (sizeof(n) >= 8) + { + if (!(n & 0xffffffff)) + { + k |= 32; + n >>= 32; + } + } + + if (!(n & 0xffff)) + { + k |= 16; + n >>= 16; + } + + if (!(n & 0xff)) + { + k |= 8; + n >>= 8; + } + + if (!(n & 0xf)) + { + k |= 4; + n >>= 4; + } + + if (!(n & 0x3)) + { + k |= 2; + n >>= 2; + } + + if (!(n & 0x1)) + k++; + + return k; +} + +/** Queue manipulation macros + (doubly linked circular list) + (complexity O(1)) +*/ +#define QUEUE_STARTED_NAMED(entry, name) \ + (((entry)->m_##name##next == (entry)) && ((entry)->m_##name##prev == (entry))) + +#define QUEUE_START_NAMED(entry, name) \ +{ \ + (entry)->m_##name##next = (entry); \ + (entry)->m_##name##prev = (entry); \ +} + +#define QUEUE_REMOVE_NAMED(entry, name) \ +{ \ + (entry)->m_##name##prev->m_##name##next = (entry)->m_##name##next; \ + (entry)->m_##name##next->m_##name##prev = (entry)->m_##name##prev; \ + QUEUE_START_NAMED(entry, name); \ +} + +#define QUEUE_INSERT_HEAD_NAMED(head, entry, name) \ +{ \ + (entry)->m_##name##prev = (head); \ + (entry)->m_##name##next = (head)->m_##name##next; \ + (head)->m_##name##next = (entry); \ + (entry)->m_##name##next->m_##name##prev = (entry); \ +} + +#define QUEUE_INSERT_TAIL_NAMED(head, entry, name) \ +{ \ + (entry)->m_##name##next = (head); \ + (entry)->m_##name##prev = (head)->m_##name##prev; \ + (head)->m_##name##prev = (entry); \ + (entry)->m_##name##prev->m_##name##next = (entry); \ +} + +#if defined(SAL_UNX) + +#include <unistd.h> +#include <pthread.h> + +typedef pthread_mutex_t rtl_memory_lock_type; + +#define RTL_MEMORY_LOCK_INIT(lock) pthread_mutex_init((lock), nullptr) +#define RTL_MEMORY_LOCK_DESTROY(lock) pthread_mutex_destroy((lock)) + +#define RTL_MEMORY_LOCK_ACQUIRE(lock) pthread_mutex_lock((lock)) +#define RTL_MEMORY_LOCK_RELEASE(lock) pthread_mutex_unlock((lock)) + +#elif defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +typedef CRITICAL_SECTION rtl_memory_lock_type; + +#define RTL_MEMORY_LOCK_INIT(lock) InitializeCriticalSection((lock)) +#define RTL_MEMORY_LOCK_DESTROY(lock) DeleteCriticalSection((lock)) + +#define RTL_MEMORY_LOCK_ACQUIRE(lock) EnterCriticalSection((lock)) +#define RTL_MEMORY_LOCK_RELEASE(lock) LeaveCriticalSection((lock)) + +#else +#error Unknown platform +#endif /* SAL_UNX | _WIN32 */ + +/** Cache creation flags. + @internal +*/ +#define RTL_CACHE_FLAG_NOMAGAZINE (1 << 13) /* w/o magazine layer */ + +#endif // INCLUDED_SAL_RTL_ALLOC_IMPL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/bootstrap.cxx b/sal/rtl/bootstrap.cxx new file mode 100644 index 000000000..12558d319 --- /dev/null +++ b/sal/rtl/bootstrap.cxx @@ -0,0 +1,992 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include <config_features.h> +#include <config_folders.h> + +#include <rtl/bootstrap.h> +#include <rtl/bootstrap.hxx> +#include <osl/diagnose.h> +#include <osl/module.h> +#include <osl/process.h> +#include <osl/file.hxx> +#include <osl/mutex.hxx> +#include <osl/profile.hxx> +#include <osl/security.hxx> +#include <rtl/alloc.h> +#include <rtl/string.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <rtl/byteseq.hxx> +#include <rtl/malformeduriexception.hxx> +#include <rtl/uri.hxx> +#include <sal/log.hxx> +#include <o3tl/lru_map.hxx> +#include <o3tl/string_view.hxx> + +#include <utility> +#include <vector> +#include <algorithm> +#include <cstddef> +#include <string_view> +#include <unordered_map> + +#ifdef ANDROID +#include <osl/detail/android-bootstrap.h> +#endif + +#ifdef IOS +#include <premac.h> +#import <Foundation/Foundation.h> +#include <postmac.h> +#endif + +using osl::DirectoryItem; +using osl::FileStatus; + +namespace +{ + +struct Bootstrap_Impl; + +constexpr std::u16string_view VND_SUN_STAR_PATHNAME = u"vnd.sun.star.pathname:"; + +bool isPathnameUrl(std::u16string_view url) +{ + return o3tl::matchIgnoreAsciiCase(url, VND_SUN_STAR_PATHNAME); +} + +bool resolvePathnameUrl(OUString * url) +{ + OSL_ASSERT(url); + if (!isPathnameUrl(*url) || + (osl::FileBase::getFileURLFromSystemPath( + url->copy(VND_SUN_STAR_PATHNAME.size()), *url) == + osl::FileBase::E_None)) + { + return true; + } + *url = OUString(); + return false; +} + +enum LookupMode { + LOOKUP_MODE_NORMAL, LOOKUP_MODE_URE_BOOTSTRAP, + LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION }; + +struct ExpandRequestLink { + ExpandRequestLink const * next; + Bootstrap_Impl const * file; + OUString key; +}; + +OUString expandMacros( + Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode, + ExpandRequestLink const * requestStack); + +OUString recursivelyExpandMacros( + Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode, + Bootstrap_Impl const * requestFile, OUString const & requestKey, + ExpandRequestLink const * requestStack) +{ + for (; requestStack; requestStack = requestStack->next) { + if (requestStack->file == requestFile && + requestStack->key == requestKey) + { + return "***RECURSION DETECTED***"; + } + } + ExpandRequestLink link = { requestStack, requestFile, requestKey }; + return expandMacros(file, text, mode, &link); +} + +struct rtl_bootstrap_NameValue +{ + OUString sName; + OUString sValue; + + rtl_bootstrap_NameValue() + {} + rtl_bootstrap_NameValue(OUString name, OUString value ) + : sName(std::move( name )), + sValue(std::move( value )) + {} +}; + +} // end namespace + +typedef std::vector<rtl_bootstrap_NameValue> NameValueVector; + +static bool find( + NameValueVector const & vector, OUString const & key, + OUString * value) +{ + OSL_ASSERT(value); + auto i = std::find_if(vector.begin(), vector.end(), + [&key](const rtl_bootstrap_NameValue& rNameValue) { return rNameValue.sName == key; }); + if (i != vector.end()) + { + *value = i->sValue; + return true; + } + return false; +} + +namespace +{ + NameValueVector rtl_bootstrap_set_vector; +} + +static bool getFromCommandLineArgs( + OUString const & key, OUString * value ) +{ + OSL_ASSERT(value); + + static NameValueVector nameValueVector = []() + { + NameValueVector tmp; + + sal_Int32 nArgCount = osl_getCommandArgCount(); + for(sal_Int32 i = 0; i < nArgCount; ++ i) + { + OUString pArg; + osl_getCommandArg( i, &pArg.pData ); + if( (pArg.startsWith("-") || pArg.startsWith("/") ) && + pArg.match("env:", 1) ) + { + sal_Int32 nIndex = pArg.indexOf( '=' ); + + if( nIndex >= 0 ) + { + rtl_bootstrap_NameValue nameValue; + nameValue.sName = pArg.copy( 5, nIndex - 5 ); + nameValue.sValue = pArg.copy( nIndex+1 ); + + if( i == nArgCount-1 && + nameValue.sValue.getLength() && + nameValue.sValue[nameValue.sValue.getLength()-1] == 13 ) + { + // avoid the 13 linefeed for the last argument, + // when the executable is started from a script, + // that was edited on windows + nameValue.sValue = nameValue.sValue.copy(0,nameValue.sValue.getLength()-1); + } + + tmp.push_back( nameValue ); + } + } + }; + return tmp; + }(); + + return find(nameValueVector, key, value); +} + +static void getExecutableDirectory_Impl(rtl_uString ** ppDirURL) +{ + OUString fileName; + osl_getExecutableFile(&(fileName.pData)); + + sal_Int32 nDirEnd = fileName.lastIndexOf('/'); + OSL_ENSURE(nDirEnd >= 0, "Cannot locate executable directory"); + + rtl_uString_newFromStr_WithLength(ppDirURL,fileName.getStr(),nDirEnd); +} + +static OUString & getIniFileName_Impl() +{ + static OUString aStaticName = []() { + OUString fileName; + +#if defined IOS + // On iOS hardcode the inifile as "rc" in the .app + // directory. Apps are self-contained anyway, there is no + // possibility to have several "applications" in the same + // installation location with different inifiles. + const char *inifile = [[@"vnd.sun.star.pathname:" stringByAppendingString: [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: @"rc"]] UTF8String]; + fileName = OUString(inifile, strlen(inifile), RTL_TEXTENCODING_UTF8); + resolvePathnameUrl(&fileName); +#elif defined ANDROID + // Apps are self-contained on Android, too, can as well hardcode + // it as "rc" in the "/assets" directory, i.e. inside the app's + // .apk (zip) archive as the /assets/rc file. + fileName = OUString("vnd.sun.star.pathname:/assets/rc"); + resolvePathnameUrl(&fileName); +#elif defined(EMSCRIPTEN) + fileName = OUString("vnd.sun.star.pathname:/instdir/program/sofficerc"); + resolvePathnameUrl(&fileName); +#else + if (getFromCommandLineArgs("INIFILENAME", &fileName)) + { + resolvePathnameUrl(&fileName); + } + else + { + osl_getExecutableFile(&(fileName.pData)); + + // get rid of a potential executable extension + OUString progExt = ".bin"; + if (fileName.getLength() > progExt.getLength() + && o3tl::equalsIgnoreAsciiCase(fileName.subView(fileName.getLength() - progExt.getLength()), progExt)) + { + fileName = fileName.copy(0, fileName.getLength() - progExt.getLength()); + } + + progExt = ".exe"; + if (fileName.getLength() > progExt.getLength() + && o3tl::equalsIgnoreAsciiCase(fileName.subView(fileName.getLength() - progExt.getLength()), progExt)) + { + fileName = fileName.copy(0, fileName.getLength() - progExt.getLength()); + } + + // append config file suffix + fileName += SAL_CONFIGFILE(""); + +#ifdef MACOSX + // We keep only executables in the MacOS folder, and all + // rc files in LIBO_ETC_FOLDER (typically "Resources"). + sal_Int32 off = fileName.lastIndexOf( "/MacOS/" ); + if (off != -1) + fileName = fileName.replaceAt(off + 1, strlen("MacOS"), u"" LIBO_ETC_FOLDER); +#endif + } +#endif + + return fileName; + }(); + + return aStaticName; +} + +// ensure the given file url has no final slash + +static void EnsureNoFinalSlash (OUString & url) +{ + sal_Int32 i = url.getLength(); + + if (i > 0 && url[i - 1] == '/') + url = url.copy(0, i - 1); +} + +namespace { + +struct Bootstrap_Impl +{ + sal_Int32 _nRefCount; + Bootstrap_Impl * _base_ini; + + NameValueVector _nameValueVector; + OUString _iniName; + + explicit Bootstrap_Impl (OUString const & rIniName); + ~Bootstrap_Impl(); + + static void * operator new (std::size_t n) + { return malloc (sal_uInt32(n)); } + static void operator delete (void * p , std::size_t) + { free (p); } + + bool getValue( + OUString const & key, rtl_uString ** value, + rtl_uString * defaultValue, LookupMode mode, bool override, + ExpandRequestLink const * requestStack) const; + bool getDirectValue( + OUString const & key, rtl_uString ** value, LookupMode mode, + ExpandRequestLink const * requestStack) const; + bool getAmbienceValue( + OUString const & key, rtl_uString ** value, LookupMode mode, + ExpandRequestLink const * requestStack) const; + void expandValue( + rtl_uString ** value, OUString const & text, LookupMode mode, + Bootstrap_Impl const * requestFile, OUString const & requestKey, + ExpandRequestLink const * requestStack) const; +}; + +} + +Bootstrap_Impl::Bootstrap_Impl( OUString const & rIniName ) + : _nRefCount( 0 ), + _base_ini( nullptr ), + _iniName (rIniName) +{ + OUString base_ini(getIniFileName_Impl()); + // normalize path + FileStatus status( osl_FileStatus_Mask_FileURL ); + DirectoryItem dirItem; + if (DirectoryItem::get(base_ini, dirItem) == DirectoryItem::E_None && + dirItem.getFileStatus(status) == DirectoryItem::E_None) + { + base_ini = status.getFileURL(); + if (rIniName != base_ini) + { + _base_ini = static_cast< Bootstrap_Impl * >( + rtl_bootstrap_args_open(base_ini.pData)); + } + } + SAL_INFO("sal.bootstrap", "Bootstrap_Impl(): sFile=" << _iniName); + oslFileHandle handle; + if (!_iniName.isEmpty() && + osl_openFile(_iniName.pData, &handle, osl_File_OpenFlag_Read) == osl_File_E_None) + { + rtl::ByteSequence seq; + + while (osl_readLine(handle , reinterpret_cast<sal_Sequence **>(&seq)) == osl_File_E_None) + { + OString line(reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength()); + sal_Int32 nIndex = line.indexOf('='); + if (nIndex >= 1) + { + struct rtl_bootstrap_NameValue nameValue; + nameValue.sName = OStringToOUString(o3tl::trim(line.subView(0,nIndex)), RTL_TEXTENCODING_ASCII_US); + nameValue.sValue = OStringToOUString(o3tl::trim(line.subView(nIndex+1)), RTL_TEXTENCODING_UTF8); + + SAL_INFO("sal.bootstrap", "pushing: name=" << nameValue.sName << " value=" << nameValue.sValue); + + _nameValueVector.push_back(nameValue); + } + } + osl_closeFile(handle); + } + else + { + SAL_INFO( "sal.bootstrap", "couldn't open file: " << _iniName ); + } +} + +Bootstrap_Impl::~Bootstrap_Impl() +{ + if (_base_ini) + rtl_bootstrap_args_close( _base_ini ); +} + +namespace { + +Bootstrap_Impl * get_static_bootstrap_handle() +{ + static Bootstrap_Impl* s_handle = []() { + OUString iniName(getIniFileName_Impl()); + Bootstrap_Impl* that = static_cast<Bootstrap_Impl*>(rtl_bootstrap_args_open(iniName.pData)); + if (!that) + { + that = new Bootstrap_Impl(iniName); + ++that->_nRefCount; + } + return that; + }(); + + return s_handle; +} + +struct FundamentalIniData +{ + rtlBootstrapHandle ini; + + FundamentalIniData() + { + OUString uri; + ini = + (get_static_bootstrap_handle()->getValue( + "URE_BOOTSTRAP", &uri.pData, nullptr, LOOKUP_MODE_NORMAL, false, + nullptr) + && resolvePathnameUrl(&uri)) + ? rtl_bootstrap_args_open(uri.pData) : nullptr; + } + + ~FundamentalIniData() { rtl_bootstrap_args_close(ini); } + + FundamentalIniData(const FundamentalIniData&) = delete; + FundamentalIniData& operator=(const FundamentalIniData&) = delete; +}; + +FundamentalIniData& FundamentalIni() +{ + static FundamentalIniData SINGLETON; + return SINGLETON; +} + +} + +bool Bootstrap_Impl::getValue( + OUString const & key, rtl_uString ** value, rtl_uString * defaultValue, + LookupMode mode, bool override, ExpandRequestLink const * requestStack) + const +{ + if (mode == LOOKUP_MODE_NORMAL && key == "URE_BOOTSTRAP") + mode = LOOKUP_MODE_URE_BOOTSTRAP; + + if (override && getDirectValue(key, value, mode, requestStack)) + return true; + + if (key == "_OS") + { + rtl_uString_assign( + value, OUString(RTL_OS).pData); + return true; + } + + if (key == "_ARCH") + { + rtl_uString_assign( + value, OUString(RTL_ARCH).pData); + return true; + } + + if (key == "_CPPU_ENV") + { + rtl_uString_assign( + value, + (OUString( + SAL_STRINGIFY(CPPU_ENV)). + pData)); + return true; + } + +#ifdef ANDROID + if (key == "APP_DATA_DIR") + { + const char *app_data_dir = lo_get_app_data_dir(); + rtl_uString_assign( + value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData); + return true; + } +#endif + +#ifdef IOS + if (key == "APP_DATA_DIR") + { + const char *app_data_dir = [[[[NSBundle mainBundle] bundlePath] stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLPathAllowedCharacterSet]] UTF8String]; + rtl_uString_assign( + value, OUString(app_data_dir, strlen(app_data_dir), RTL_TEXTENCODING_UTF8).pData); + return true; + } +#endif + + if (key == "ORIGIN") + { + rtl_uString_assign( + value, + _iniName.copy( + 0, std::max<sal_Int32>(0, _iniName.lastIndexOf('/'))).pData); + return true; + } + + if (getAmbienceValue(key, value, mode, requestStack)) + return true; + + if (key == "SYSUSERCONFIG") + { + OUString v; + bool b = osl::Security().getConfigDir(v); + EnsureNoFinalSlash(v); + rtl_uString_assign(value, v.pData); + return b; + } + + if (key == "SYSUSERHOME") + { + OUString v; + bool b = osl::Security().getHomeDir(v); + EnsureNoFinalSlash(v); + rtl_uString_assign(value, v.pData); + return b; + } + + if (key == "SYSBINDIR") + { + getExecutableDirectory_Impl(value); + return true; + } + + if (_base_ini != nullptr && _base_ini->getDirectValue(key, value, mode, requestStack)) + return true; + + if (!override && getDirectValue(key, value, mode, requestStack)) + return true; + + if (mode == LOOKUP_MODE_NORMAL) + { + FundamentalIniData const & d = FundamentalIni(); + Bootstrap_Impl const * b = static_cast<Bootstrap_Impl const *>(d.ini); + if (b != nullptr && b != this && b->getDirectValue(key, value, mode, requestStack)) + return true; + } + + if (defaultValue != nullptr) + { + rtl_uString_assign(value, defaultValue); + return true; + } + + rtl_uString_new(value); + return false; +} + +bool Bootstrap_Impl::getDirectValue( + OUString const & key, rtl_uString ** value, LookupMode mode, + ExpandRequestLink const * requestStack) const +{ + OUString v; + if (find(_nameValueVector, key, &v)) + { + expandValue(value, v, mode, this, key, requestStack); + return true; + } + + return false; +} + +bool Bootstrap_Impl::getAmbienceValue( + OUString const & key, rtl_uString ** value, LookupMode mode, + ExpandRequestLink const * requestStack) const +{ + OUString v; + bool f; + + { + osl::MutexGuard g(osl::Mutex::getGlobalMutex()); + f = find(rtl_bootstrap_set_vector, key, &v); + } + + if (f || getFromCommandLineArgs(key, &v) || + osl_getEnvironment(key.pData, &v.pData) == osl_Process_E_None) + { + expandValue(value, v, mode, nullptr, key, requestStack); + return true; + } + + return false; +} + +void Bootstrap_Impl::expandValue( + rtl_uString ** value, OUString const & text, LookupMode mode, + Bootstrap_Impl const * requestFile, OUString const & requestKey, + ExpandRequestLink const * requestStack) const +{ + rtl_uString_assign( + value, + (mode == LOOKUP_MODE_URE_BOOTSTRAP && isPathnameUrl(text) ? + text : + recursivelyExpandMacros( + this, text, + (mode == LOOKUP_MODE_URE_BOOTSTRAP ? + LOOKUP_MODE_URE_BOOTSTRAP_EXPANSION : mode), + requestFile, requestKey, requestStack)).pData); +} + +namespace { + +typedef std::unordered_map< OUString, Bootstrap_Impl * > bootstrap_map_t; +bootstrap_map_t bootstrap_map; + +} + +rtlBootstrapHandle SAL_CALL rtl_bootstrap_args_open(rtl_uString * pIniName) +{ + static o3tl::lru_map<OUString,OUString> fileUrlLookupCache(16); + + OUString originalIniName( pIniName ); + OUString iniName; + + osl::ResettableMutexGuard guard(osl::Mutex::getGlobalMutex()); + auto cacheIt = fileUrlLookupCache.find(originalIniName); + bool foundInCache = cacheIt != fileUrlLookupCache.end(); + if (foundInCache) + iniName = cacheIt->second; + guard.clear(); + + // normalize path + if (!foundInCache) + { + FileStatus status(osl_FileStatus_Mask_FileURL); + DirectoryItem dirItem; + if (DirectoryItem::get(originalIniName, dirItem) != DirectoryItem::E_None || + dirItem.getFileStatus(status) != DirectoryItem::E_None) + { + return nullptr; + } + iniName = status.getFileURL(); + } + + guard.reset(); + if (!foundInCache) + fileUrlLookupCache.insert({originalIniName, iniName}); + Bootstrap_Impl * that; + auto iFind(bootstrap_map.find(iniName)); + if (iFind == bootstrap_map.end()) + { + guard.clear(); + that = new Bootstrap_Impl(iniName); + guard.reset(); + iFind = bootstrap_map.find(iniName); + if (iFind == bootstrap_map.end()) + { + ++that->_nRefCount; + ::std::pair< bootstrap_map_t::iterator, bool > insertion( + bootstrap_map.emplace(iniName, that)); + OSL_ASSERT(insertion.second); + } + else + { + Bootstrap_Impl * obsolete = that; + that = iFind->second; + ++that->_nRefCount; + guard.clear(); + delete obsolete; + } + } + else + { + that = iFind->second; + ++that->_nRefCount; + } + return static_cast< rtlBootstrapHandle >( that ); +} + +void SAL_CALL rtl_bootstrap_args_close(rtlBootstrapHandle handle) SAL_THROW_EXTERN_C() +{ + if (!handle) + return; + + Bootstrap_Impl * that = static_cast< Bootstrap_Impl * >( handle ); + + osl::MutexGuard guard(osl::Mutex::getGlobalMutex()); + OSL_ASSERT(bootstrap_map.find(that->_iniName)->second == that); + --that->_nRefCount; + + if (that->_nRefCount != 0) + return; + + std::size_t const nLeaking = 8; // only hold up to 8 files statically + if (bootstrap_map.size() > nLeaking) + { + ::std::size_t erased = bootstrap_map.erase( that->_iniName ); + if (erased != 1) { + OSL_ASSERT( false ); + } + delete that; + } +} + +sal_Bool SAL_CALL rtl_bootstrap_get_from_handle( + rtlBootstrapHandle handle, + rtl_uString * pName, + rtl_uString ** ppValue, + rtl_uString * pDefault +) +{ + osl::MutexGuard guard(osl::Mutex::getGlobalMutex()); + + bool found = false; + if(ppValue && pName) + { + if (!handle) + handle = get_static_bootstrap_handle(); + + found = static_cast< Bootstrap_Impl * >(handle)->getValue( + pName, ppValue, pDefault, LOOKUP_MODE_NORMAL, false, nullptr ); + } + + return found; +} + +void SAL_CALL rtl_bootstrap_get_iniName_from_handle ( + rtlBootstrapHandle handle, + rtl_uString ** ppIniName +) +{ + if(!ppIniName) + return; + + if(handle) + { + Bootstrap_Impl * pImpl = static_cast<Bootstrap_Impl*>(handle); + rtl_uString_assign(ppIniName, pImpl->_iniName.pData); + } + else + { + const OUString & iniName = getIniFileName_Impl(); + rtl_uString_assign(ppIniName, iniName.pData); + } +} + +void SAL_CALL rtl_bootstrap_setIniFileName ( + rtl_uString * pName +) +{ + osl::MutexGuard guard(osl::Mutex::getGlobalMutex()); + OUString & file = getIniFileName_Impl(); + file = pName; +} + +sal_Bool SAL_CALL rtl_bootstrap_get ( + rtl_uString * pName, + rtl_uString ** ppValue, + rtl_uString * pDefault +) +{ + return rtl_bootstrap_get_from_handle(nullptr, pName, ppValue, pDefault); +} + +void SAL_CALL rtl_bootstrap_set ( + rtl_uString * pName, + rtl_uString * pValue +) +{ + const OUString name(pName); + const OUString value(pValue); + + osl::MutexGuard guard(osl::Mutex::getGlobalMutex()); + + for (auto & item : rtl_bootstrap_set_vector) + { + if (item.sName == name) + { + item.sValue = value; + return; + } + } + + SAL_INFO("sal.bootstrap", "explicitly getting: name=" << name << " value=" <<value); + + rtl_bootstrap_set_vector.emplace_back(name, value); +} + +void SAL_CALL rtl_bootstrap_expandMacros_from_handle( + rtlBootstrapHandle handle, + rtl_uString ** macro) +{ + if (!handle) + handle = get_static_bootstrap_handle(); + + OUString expanded(expandMacros(static_cast< Bootstrap_Impl * >(handle), + OUString::unacquired(macro), + LOOKUP_MODE_NORMAL, nullptr)); + rtl_uString_assign(macro, expanded.pData); +} + +void SAL_CALL rtl_bootstrap_expandMacros(rtl_uString ** macro) +{ + rtl_bootstrap_expandMacros_from_handle(nullptr, macro); +} + +void rtl_bootstrap_encode(rtl_uString const * value, rtl_uString ** encoded) +{ + OSL_ASSERT(value); + OUStringBuffer b(value->length+5); + for (sal_Int32 i = 0; i < value->length; ++i) + { + sal_Unicode c = value->buffer[i]; + if (c == '$' || c == '\\') + b.append('\\'); + + b.append(c); + } + + rtl_uString_assign(encoded, b.makeStringAndClear().pData); +} + +namespace { + +int hex(sal_Unicode c) +{ + return + c >= '0' && c <= '9' ? c - '0' : + c >= 'A' && c <= 'F' ? c - 'A' + 10 : + c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1; +} + +sal_Unicode read(std::u16string_view text, std::size_t * pos, bool * escaped) +{ + OSL_ASSERT(pos && *pos < text.length() && escaped); + sal_Unicode c = text[(*pos)++]; + if (c == '\\') + { + int n1, n2, n3, n4; + if (*pos < text.length() - 4 && text[*pos] == 'u' && + ((n1 = hex(text[*pos + 1])) >= 0) && + ((n2 = hex(text[*pos + 2])) >= 0) && + ((n3 = hex(text[*pos + 3])) >= 0) && + ((n4 = hex(text[*pos + 4])) >= 0)) + { + *pos += 5; + *escaped = true; + return static_cast< sal_Unicode >( + (n1 << 12) | (n2 << 8) | (n3 << 4) | n4); + } + + if (*pos < text.length()) + { + *escaped = true; + return text[(*pos)++]; + } + } + + *escaped = false; + return c; +} + +OUString lookup( + Bootstrap_Impl const * file, LookupMode mode, bool override, + OUString const & key, ExpandRequestLink const * requestStack) +{ + OUString v; + (file == nullptr ? get_static_bootstrap_handle() : file)->getValue( + key, &v.pData, nullptr, mode, override, requestStack); + return v; +} + +OUString expandMacros( + Bootstrap_Impl const * file, std::u16string_view text, LookupMode mode, + ExpandRequestLink const * requestStack) +{ + SAL_INFO("sal.bootstrap", "expandMacros called with: " << OUString(text)); + OUStringBuffer buf(2048); + + for (std::size_t i = 0; i < text.length();) + { + bool escaped; + sal_Unicode c = read(text, &i, &escaped); + if (escaped || c != '$') + { + buf.append(c); + } + else + { + if (i < text.length() && text[i] == '{') + { + ++i; + std::size_t p = i; + sal_Int32 nesting = 0; + OUString seg[3]; + int n = 0; + + while (i < text.length()) + { + std::size_t j = i; + c = read(text, &i, &escaped); + + if (!escaped) + { + switch (c) + { + case '{': + ++nesting; + break; + case '}': + if (nesting == 0) + { + seg[n++] = text.substr(p, j - p); + goto done; + } + else + { + --nesting; + } + break; + case ':': + if (nesting == 0 && n < 2) + { + seg[n++] = text.substr(p, j - p); + p = i; + } + break; + } + } + } + done: + for (int j = 0; j < n; ++j) + { + seg[j] = expandMacros(file, seg[j], mode, requestStack); + } + + if (n == 3 && seg[0] != ".override" && seg[1].isEmpty()) + { + // For backward compatibility, treat ${file::key} the + // same as just ${file:key}: + seg[1] = seg[2]; + n = 2; + } + + if (n == 1) + { + buf.append(lookup(file, mode, false, seg[0], requestStack)); + } + else if (n == 2) + { + rtl::Bootstrap b(seg[0]); + Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle()); + buf.append(lookup(f, mode, false, seg[1], requestStack)); + } + else if (n == 3 && seg[0] == ".override") + { + rtl::Bootstrap b(seg[1]); + Bootstrap_Impl * f = static_cast< Bootstrap_Impl * >(b.getHandle()); + buf.append(lookup(f, mode, f != nullptr, seg[2], requestStack)); + } + else + { + // Going through osl::Profile, this code erroneously + // does not recursively expand macros in the resulting + // replacement text (and if it did, it would fail to + // detect cycles that pass through here): + buf.append( + OStringToOUString( + osl::Profile(seg[0]).readString( + OUStringToOString( + seg[1], RTL_TEXTENCODING_UTF8), + OUStringToOString( + seg[2], RTL_TEXTENCODING_UTF8), + OString()), + RTL_TEXTENCODING_UTF8)); + } + } + else + { + OUStringBuffer kbuf(sal_Int32(text.length())); + for (; i < text.length();) + { + std::size_t j = i; + c = read(text, &j, &escaped); + if (!escaped && + (c == ' ' || c == '$' || c == '-' || c == '/' || + c == ';' || c == '\\')) + { + break; + } + + kbuf.append(c); + i = j; + } + + buf.append( + lookup( + file, mode, false, kbuf.makeStringAndClear(), + requestStack)); + } + } + } + + OUString result(buf.makeStringAndClear()); + SAL_INFO("sal.bootstrap", "expandMacros result: " << result); + + return result; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/byteseq.cxx b/sal/rtl/byteseq.cxx new file mode 100644 index 000000000..3c4967d54 --- /dev/null +++ b/sal/rtl/byteseq.cxx @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <assert.h> +#include <string.h> +#include <stdlib.h> + +#include <osl/diagnose.h> +#include <osl/interlck.h> + +#include <rtl/byteseq.h> + +/* static data to be referenced by all empty strings + * the refCount is predefined to 1 and must never become 0 ! + */ +static sal_Sequence aEmpty_rtl_ByteSeq = +{ + 1, /* sal_Int32 refCount; */ + 0, /* sal_Int32 length; */ + { 0 } /* sal_Unicode buffer[1]; */ +}; + +void SAL_CALL rtl_byte_sequence_reference2One( + sal_Sequence ** ppSequence ) SAL_THROW_EXTERN_C() +{ + sal_Sequence * pSequence; + + OSL_ENSURE( ppSequence, "### null ptr!" ); + pSequence = *ppSequence; + + if (pSequence->nRefCount > 1) + { + sal_Sequence *pNew; + sal_Int32 nElements = pSequence->nElements; + if (nElements) + { + pNew = static_cast<sal_Sequence *>(malloc( SAL_SEQUENCE_HEADER_SIZE + nElements )); + + if ( pNew != nullptr ) + memcpy( pNew->elements, pSequence->elements, nElements ); + + if (! osl_atomic_decrement( &pSequence->nRefCount )) + free( pSequence ); + } + else + { + pNew = static_cast<sal_Sequence *>(malloc( SAL_SEQUENCE_HEADER_SIZE )); + } + + if ( pNew != nullptr ) + { + pNew->nRefCount = 1; + pNew->nElements = nElements; + } + + *ppSequence = pNew; + } +} + +void SAL_CALL rtl_byte_sequence_realloc( + sal_Sequence ** ppSequence, sal_Int32 nSize ) SAL_THROW_EXTERN_C() +{ + sal_Sequence * pSequence; + sal_Int32 nElements; + + assert(ppSequence && "### null ptr!"); + pSequence = *ppSequence; + nElements = pSequence->nElements; + + if (nElements == nSize) + return; + + if (pSequence->nRefCount > 1) // split + { + sal_Sequence *pNew = static_cast<sal_Sequence *>(malloc( SAL_SEQUENCE_HEADER_SIZE + nSize )); + + if ( pNew != nullptr ) + { + if (nSize > nElements) + { + memcpy( pNew->elements, pSequence->elements, nElements ); + memset( pNew->elements + nElements, 0, nSize - nElements ); + } + else + { + memcpy( pNew->elements, pSequence->elements, nSize ); + } + } + + if (! osl_atomic_decrement( &pSequence->nRefCount )) + free( pSequence ); + pSequence = pNew; + } + else + { + pSequence = static_cast<sal_Sequence *>(realloc( + pSequence, SAL_SEQUENCE_HEADER_SIZE + nSize )); + } + + if ( pSequence != nullptr ) + { + pSequence->nRefCount = 1; + pSequence->nElements = nSize; + } + + *ppSequence = pSequence; +} + +void SAL_CALL rtl_byte_sequence_acquire( sal_Sequence *pSequence ) + SAL_THROW_EXTERN_C() +{ + OSL_ASSERT( pSequence ); + osl_atomic_increment( &(pSequence->nRefCount) ); +} + +void SAL_CALL rtl_byte_sequence_release( sal_Sequence *pSequence ) + SAL_THROW_EXTERN_C() +{ + if ( pSequence != nullptr ) + { + if (! osl_atomic_decrement( &(pSequence->nRefCount )) ) + { + free( pSequence ); + } + } +} + +void SAL_CALL rtl_byte_sequence_construct( sal_Sequence **ppSequence , sal_Int32 nLength ) + SAL_THROW_EXTERN_C() +{ + OSL_ASSERT( ppSequence ); + if( *ppSequence ) + { + rtl_byte_sequence_release( *ppSequence ); + *ppSequence = nullptr; + } + + if( nLength ) + { + *ppSequence = static_cast<sal_Sequence *>(rtl_allocateZeroMemory( SAL_SEQUENCE_HEADER_SIZE + nLength )); + + if ( *ppSequence != nullptr ) + { + (*ppSequence)->nRefCount = 1; + (*ppSequence)->nElements = nLength; + } + } + else + { + *ppSequence = &aEmpty_rtl_ByteSeq; + rtl_byte_sequence_acquire( *ppSequence ); + } +} + +void SAL_CALL rtl_byte_sequence_constructNoDefault( sal_Sequence **ppSequence , sal_Int32 nLength ) + SAL_THROW_EXTERN_C() +{ + OSL_ASSERT( ppSequence ); + if( *ppSequence ) + { + rtl_byte_sequence_release( *ppSequence ); + *ppSequence = nullptr; + } + + *ppSequence = static_cast<sal_Sequence *>(malloc( SAL_SEQUENCE_HEADER_SIZE + nLength )); + + if ( *ppSequence != nullptr ) + { + (*ppSequence)->nRefCount = 1; + (*ppSequence)->nElements = nLength; + } +} + +void SAL_CALL rtl_byte_sequence_constructFromArray( + sal_Sequence **ppSequence, const sal_Int8 *pData , sal_Int32 nLength ) + SAL_THROW_EXTERN_C() +{ + rtl_byte_sequence_constructNoDefault( ppSequence , nLength ); + if ( *ppSequence != nullptr && nLength != 0 ) + memcpy( (*ppSequence)->elements, pData, nLength ); +} + +void SAL_CALL rtl_byte_sequence_assign( sal_Sequence **ppSequence , sal_Sequence *pSequence ) + SAL_THROW_EXTERN_C() +{ + if ( *ppSequence != pSequence) + { + if( *ppSequence ) + { + rtl_byte_sequence_release( *ppSequence ); + } + *ppSequence = pSequence; + rtl_byte_sequence_acquire( *ppSequence ); + } +// else +// nothing to do + +} + +sal_Bool SAL_CALL rtl_byte_sequence_equals( sal_Sequence *pSequence1 , sal_Sequence *pSequence2 ) + SAL_THROW_EXTERN_C() +{ + assert(pSequence1 && pSequence2); + if (pSequence1 == pSequence2) + { + return true; + } + if (pSequence1->nElements != pSequence2->nElements) + { + return false; + } + return + memcmp( + pSequence1->elements, pSequence2->elements, pSequence1->nElements ) + == 0; +} + +const sal_Int8 *SAL_CALL rtl_byte_sequence_getConstArray( sal_Sequence *pSequence ) + SAL_THROW_EXTERN_C() +{ + return reinterpret_cast<sal_Int8*>(pSequence->elements); +} + +sal_Int32 SAL_CALL rtl_byte_sequence_getLength( sal_Sequence *pSequence ) + SAL_THROW_EXTERN_C() +{ + return pSequence->nElements; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/cipher.cxx b/sal/rtl/cipher.cxx new file mode 100644 index 000000000..bb0884b7a --- /dev/null +++ b/sal/rtl/cipher.cxx @@ -0,0 +1,1433 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <string.h> + +#include <o3tl/safeint.hxx> +#include <sal/types.h> +#include <rtl/alloc.h> +#include <rtl/cipher.h> +#include <algorithm> +#include <cassert> +#include <cstring> +#include <limits> + +#if defined LIBO_CIPHER_OPENSSL_BACKEND +#include <openssl/evp.h> +#endif + +#if !defined LIBO_CIPHER_OPENSSL_BACKEND +#define RTL_CIPHER_NTOHL(c, l) \ + ((l) = (static_cast<sal_uInt32>(*((c)++))) << 24, \ + (l) |= (static_cast<sal_uInt32>(*((c)++))) << 16, \ + (l) |= (static_cast<sal_uInt32>(*((c)++))) << 8, \ + (l) |= (static_cast<sal_uInt32>(*((c)++)))) + +#define RTL_CIPHER_HTONL(l, c) \ + (*((c)++) = static_cast<sal_uInt8>(((l) >> 24) & 0xff), \ + *((c)++) = static_cast<sal_uInt8>(((l) >> 16) & 0xff), \ + *((c)++) = static_cast<sal_uInt8>(((l) >> 8) & 0xff), \ + *((c)++) = static_cast<sal_uInt8>(((l) ) & 0xff)) + +#define RTL_CIPHER_NTOHL64(c, xl, xr, n) \ +{ \ + (xl) = (xr) = 0; \ + (c) += (n); \ + switch ((n)) \ + { \ + case 8: (xr) = (static_cast<sal_uInt32>(*(--(c)))); \ + [[fallthrough]]; \ + case 7: (xr) |= (static_cast<sal_uInt32>(*(--(c)))) << 8; \ + [[fallthrough]]; \ + case 6: (xr) |= (static_cast<sal_uInt32>(*(--(c)))) << 16; \ + [[fallthrough]]; \ + case 5: (xr) |= (static_cast<sal_uInt32>(*(--(c)))) << 24; \ + [[fallthrough]]; \ + case 4: (xl) = (static_cast<sal_uInt32>(*(--(c)))); \ + [[fallthrough]]; \ + case 3: (xl) |= (static_cast<sal_uInt32>(*(--(c)))) << 8; \ + [[fallthrough]]; \ + case 2: (xl) |= (static_cast<sal_uInt32>(*(--(c)))) << 16; \ + [[fallthrough]]; \ + case 1: (xl) |= (static_cast<sal_uInt32>(*(--(c)))) << 24; \ + } \ +} + +#define RTL_CIPHER_HTONL64(xl, xr, c, n) \ +{ \ + (c) += (n); \ + switch ((n)) \ + { \ + case 8: *(--(c)) = static_cast<sal_uInt8>(((xr) ) & 0xff); \ + [[fallthrough]]; \ + case 7: *(--(c)) = static_cast<sal_uInt8>(((xr) >> 8) & 0xff); \ + [[fallthrough]]; \ + case 6: *(--(c)) = static_cast<sal_uInt8>(((xr) >> 16) & 0xff); \ + [[fallthrough]]; \ + case 5: *(--(c)) = static_cast<sal_uInt8>(((xr) >> 24) & 0xff); \ + [[fallthrough]]; \ + case 4: *(--(c)) = static_cast<sal_uInt8>(((xl) ) & 0xff); \ + [[fallthrough]]; \ + case 3: *(--(c)) = static_cast<sal_uInt8>(((xl) >> 8) & 0xff); \ + [[fallthrough]]; \ + case 2: *(--(c)) = static_cast<sal_uInt8>(((xl) >> 16) & 0xff); \ + [[fallthrough]]; \ + case 1: *(--(c)) = static_cast<sal_uInt8>(((xl) >> 24) & 0xff); \ + } \ +} +#endif + +typedef rtlCipherError(cipher_init_t) ( + rtlCipher Cipher, + rtlCipherDirection Direction, + const sal_uInt8 *pKeyData, sal_Size nKeyLen, + const sal_uInt8 *pArgData, sal_Size nArgLen); + +typedef rtlCipherError(cipher_update_t) ( + rtlCipher Cipher, + const void *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen); + +typedef void (cipher_delete_t) (rtlCipher Cipher); + +namespace { + +struct Cipher_Impl +{ + rtlCipherAlgorithm m_algorithm; + rtlCipherDirection m_direction; + rtlCipherMode m_mode; + + cipher_init_t *m_init; + cipher_update_t *m_encode; + cipher_update_t *m_decode; + cipher_delete_t *m_delete; +}; + +} + +rtlCipher SAL_CALL rtl_cipher_create( + rtlCipherAlgorithm Algorithm, + rtlCipherMode Mode) SAL_THROW_EXTERN_C() +{ + rtlCipher Cipher = nullptr; + switch (Algorithm) + { + case rtl_Cipher_AlgorithmBF: + Cipher = rtl_cipher_createBF (Mode); + break; + + case rtl_Cipher_AlgorithmARCFOUR: + Cipher = rtl_cipher_createARCFOUR (Mode); + break; + + default: /* rtl_Cipher_AlgorithmInvalid */ + break; + } + return Cipher; +} + +rtlCipherError SAL_CALL rtl_cipher_init( + rtlCipher Cipher, + rtlCipherDirection Direction, + const sal_uInt8 *pKeyData, sal_Size nKeyLen, + const sal_uInt8 *pArgData, sal_Size nArgLen) SAL_THROW_EXTERN_C() +{ + Cipher_Impl *pImpl = static_cast<Cipher_Impl*>(Cipher); + if (!pImpl) + return rtl_Cipher_E_Argument; + + if (!pImpl->m_init) + return rtl_Cipher_E_Unknown; + + return (pImpl->m_init)( + Cipher, Direction, pKeyData, nKeyLen, pArgData, nArgLen); +} + +rtlCipherError SAL_CALL rtl_cipher_encode( + rtlCipher Cipher, + const void *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen) SAL_THROW_EXTERN_C() +{ + Cipher_Impl *pImpl = static_cast<Cipher_Impl*>(Cipher); + if (!pImpl) + return rtl_Cipher_E_Argument; + + if (!pImpl->m_encode) + return rtl_Cipher_E_Unknown; + + return (pImpl->m_encode)(Cipher, pData, nDatLen, pBuffer, nBufLen); +} + +rtlCipherError SAL_CALL rtl_cipher_decode( + rtlCipher Cipher, + const void *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen) SAL_THROW_EXTERN_C() +{ + Cipher_Impl *pImpl = static_cast<Cipher_Impl*>(Cipher); + if (!pImpl) + return rtl_Cipher_E_Argument; + + if (!pImpl->m_decode) + return rtl_Cipher_E_Unknown; + + return (pImpl->m_decode)(Cipher, pData, nDatLen, pBuffer, nBufLen); +} + +void SAL_CALL rtl_cipher_destroy(rtlCipher Cipher) SAL_THROW_EXTERN_C() +{ + Cipher_Impl *pImpl = static_cast<Cipher_Impl*>(Cipher); + if (pImpl && pImpl->m_delete) + pImpl->m_delete(Cipher); +} + +namespace { + +#if !defined LIBO_CIPHER_OPENSSL_BACKEND +#define CIPHER_ROUNDS_BF 16 + +struct CipherKeyBF +{ + sal_uInt32 m_S[4][256]; + sal_uInt32 m_P[CIPHER_ROUNDS_BF + 2]; +}; +#endif + +struct CipherContextBF +{ +#if defined LIBO_CIPHER_OPENSSL_BACKEND + EVP_CIPHER_CTX * m_context; +#else + CipherKeyBF m_key; + union + { + sal_uInt32 m_long[2]; + sal_uInt8 m_byte[8]; + } m_iv; + sal_uInt32 m_offset; +#endif +}; + +struct CipherBF_Impl +{ + Cipher_Impl m_cipher; + CipherContextBF m_context; +}; + +} + +#if !defined LIBO_CIPHER_OPENSSL_BACKEND +static rtlCipherError BF_init( + CipherContextBF *ctx, + rtlCipherMode eMode, + const sal_uInt8 *pKeyData, sal_Size nKeyLen, + const sal_uInt8 *pArgData, sal_Size nArgLen); +#endif + +static rtlCipherError BF_update( + CipherContextBF *ctx, + rtlCipherMode eMode, + rtlCipherDirection eDirection, + const sal_uInt8 *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen); + +#if !defined LIBO_CIPHER_OPENSSL_BACKEND +static void BF_updateECB( + CipherContextBF *ctx, + rtlCipherDirection direction, + const sal_uInt8 *pData, + sal_uInt8 *pBuffer, + sal_Size nLength); + +static void BF_updateCBC( + CipherContextBF *ctx, + rtlCipherDirection direction, + const sal_uInt8 *pData, + sal_uInt8 *pBuffer, + sal_Size nLength); + +static void BF_updateCFB( + CipherContextBF *ctx, + rtlCipherDirection direction, + const sal_uInt8 *pData, + sal_uInt8 *pBuffer); + +static void BF_encode(CipherKeyBF *key, sal_uInt32 *xl, sal_uInt32 *xr); + +static void BF_decode(CipherKeyBF *key, sal_uInt32 *xl, sal_uInt32 *xr); + +static sal_uInt32 BF(CipherKeyBF *key, sal_uInt32 x); + +const CipherKeyBF BF_key = +{ + /* S */ + { + /* S[0] */ + { + /* 0 */ + 0xD1310BA6L, 0x98DFB5ACL, 0x2FFD72DBL, 0xD01ADFB7L, + 0xB8E1AFEDL, 0x6A267E96L, 0xBA7C9045L, 0xF12C7F99L, + 0x24A19947L, 0xB3916CF7L, 0x0801F2E2L, 0x858EFC16L, + 0x636920D8L, 0x71574E69L, 0xA458FEA3L, 0xF4933D7EL, + + /* 1 */ + 0x0D95748FL, 0x728EB658L, 0x718BCD58L, 0x82154AEEL, + 0x7B54A41DL, 0xC25A59B5L, 0x9C30D539L, 0x2AF26013L, + 0xC5D1B023L, 0x286085F0L, 0xCA417918L, 0xB8DB38EFL, + 0x8E79DCB0L, 0x603A180EL, 0x6C9E0E8BL, 0xB01E8A3EL, + + /* 2 */ + 0xD71577C1L, 0xBD314B27L, 0x78AF2FDAL, 0x55605C60L, + 0xE65525F3L, 0xAA55AB94L, 0x57489862L, 0x63E81440L, + 0x55CA396AL, 0x2AAB10B6L, 0xB4CC5C34L, 0x1141E8CEL, + 0xA15486AFL, 0x7C72E993L, 0xB3EE1411L, 0x636FBC2AL, + + /* 3 */ + 0x2BA9C55DL, 0x741831F6L, 0xCE5C3E16L, 0x9B87931EL, + 0xAFD6BA33L, 0x6C24CF5CL, 0x7A325381L, 0x28958677L, + 0x3B8F4898L, 0x6B4BB9AFL, 0xC4BFE81BL, 0x66282193L, + 0x61D809CCL, 0xFB21A991L, 0x487CAC60L, 0x5DEC8032L, + + /* 4 */ + 0xEF845D5DL, 0xE98575B1L, 0xDC262302L, 0xEB651B88L, + 0x23893E81L, 0xD396ACC5L, 0x0F6D6FF3L, 0x83F44239L, + 0x2E0B4482L, 0xA4842004L, 0x69C8F04AL, 0x9E1F9B5EL, + 0x21C66842L, 0xF6E96C9AL, 0x670C9C61L, 0xABD388F0L, + + /* 5 */ + 0x6A51A0D2L, 0xD8542F68L, 0x960FA728L, 0xAB5133A3L, + 0x6EEF0B6CL, 0x137A3BE4L, 0xBA3BF050L, 0x7EFB2A98L, + 0xA1F1651DL, 0x39AF0176L, 0x66CA593EL, 0x82430E88L, + 0x8CEE8619L, 0x456F9FB4L, 0x7D84A5C3L, 0x3B8B5EBEL, + + /* 6 */ + 0xE06F75D8L, 0x85C12073L, 0x401A449FL, 0x56C16AA6L, + 0x4ED3AA62L, 0x363F7706L, 0x1BFEDF72L, 0x429B023DL, + 0x37D0D724L, 0xD00A1248L, 0xDB0FEAD3L, 0x49F1C09BL, + 0x075372C9L, 0x80991B7BL, 0x25D479D8L, 0xF6E8DEF7L, + + /* 7 */ + 0xE3FE501AL, 0xB6794C3BL, 0x976CE0BDL, 0x04C006BAL, + 0xC1A94FB6L, 0x409F60C4L, 0x5E5C9EC2L, 0x196A2463L, + 0x68FB6FAFL, 0x3E6C53B5L, 0x1339B2EBL, 0x3B52EC6FL, + 0x6DFC511FL, 0x9B30952CL, 0xCC814544L, 0xAF5EBD09L, + + /* 8 */ + 0xBEE3D004L, 0xDE334AFDL, 0x660F2807L, 0x192E4BB3L, + 0xC0CBA857L, 0x45C8740FL, 0xD20B5F39L, 0xB9D3FBDBL, + 0x5579C0BDL, 0x1A60320AL, 0xD6A100C6L, 0x402C7279L, + 0x679F25FEL, 0xFB1FA3CCL, 0x8EA5E9F8L, 0xDB3222F8L, + + /* 9 */ + 0x3C7516DFL, 0xFD616B15L, 0x2F501EC8L, 0xAD0552ABL, + 0x323DB5FAL, 0xFD238760L, 0x53317B48L, 0x3E00DF82L, + 0x9E5C57BBL, 0xCA6F8CA0L, 0x1A87562EL, 0xDF1769DBL, + 0xD542A8F6L, 0x287EFFC3L, 0xAC6732C6L, 0x8C4F5573L, + + /* A */ + 0x695B27B0L, 0xBBCA58C8L, 0xE1FFA35DL, 0xB8F011A0L, + 0x10FA3D98L, 0xFD2183B8L, 0x4AFCB56CL, 0x2DD1D35BL, + 0x9A53E479L, 0xB6F84565L, 0xD28E49BCL, 0x4BFB9790L, + 0xE1DDF2DAL, 0xA4CB7E33L, 0x62FB1341L, 0xCEE4C6E8L, + + /* B */ + 0xEF20CADAL, 0x36774C01L, 0xD07E9EFEL, 0x2BF11FB4L, + 0x95DBDA4DL, 0xAE909198L, 0xEAAD8E71L, 0x6B93D5A0L, + 0xD08ED1D0L, 0xAFC725E0L, 0x8E3C5B2FL, 0x8E7594B7L, + 0x8FF6E2FBL, 0xF2122B64L, 0x8888B812L, 0x900DF01CL, + + /* C */ + 0x4FAD5EA0L, 0x688FC31CL, 0xD1CFF191L, 0xB3A8C1ADL, + 0x2F2F2218L, 0xBE0E1777L, 0xEA752DFEL, 0x8B021FA1L, + 0xE5A0CC0FL, 0xB56F74E8L, 0x18ACF3D6L, 0xCE89E299L, + 0xB4A84FE0L, 0xFD13E0B7L, 0x7CC43B81L, 0xD2ADA8D9L, + + /* D */ + 0x165FA266L, 0x80957705L, 0x93CC7314L, 0x211A1477L, + 0xE6AD2065L, 0x77B5FA86L, 0xC75442F5L, 0xFB9D35CFL, + 0xEBCDAF0CL, 0x7B3E89A0L, 0xD6411BD3L, 0xAE1E7E49L, + 0x00250E2DL, 0x2071B35EL, 0x226800BBL, 0x57B8E0AFL, + + /* E */ + 0x2464369BL, 0xF009B91EL, 0x5563911DL, 0x59DFA6AAL, + 0x78C14389L, 0xD95A537FL, 0x207D5BA2L, 0x02E5B9C5L, + 0x83260376L, 0x6295CFA9L, 0x11C81968L, 0x4E734A41L, + 0xB3472DCAL, 0x7B14A94AL, 0x1B510052L, 0x9A532915L, + + /* F */ + 0xD60F573FL, 0xBC9BC6E4L, 0x2B60A476L, 0x81E67400L, + 0x08BA6FB5L, 0x571BE91FL, 0xF296EC6BL, 0x2A0DD915L, + 0xB6636521L, 0xE7B9F9B6L, 0xFF34052EL, 0xC5855664L, + 0x53B02D5DL, 0xA99F8FA1L, 0x08BA4799L, 0x6E85076AL + }, + + /* S[1] */ + { + 0x4B7A70E9L, 0xB5B32944L, 0xDB75092EL, 0xC4192623L, + 0xAD6EA6B0L, 0x49A7DF7DL, 0x9CEE60B8L, 0x8FEDB266L, + 0xECAA8C71L, 0x699A17FFL, 0x5664526CL, 0xC2B19EE1L, + 0x193602A5L, 0x75094C29L, 0xA0591340L, 0xE4183A3EL, + + 0x3F54989AL, 0x5B429D65L, 0x6B8FE4D6L, 0x99F73FD6L, + 0xA1D29C07L, 0xEFE830F5L, 0x4D2D38E6L, 0xF0255DC1L, + 0x4CDD2086L, 0x8470EB26L, 0x6382E9C6L, 0x021ECC5EL, + 0x09686B3FL, 0x3EBAEFC9L, 0x3C971814L, 0x6B6A70A1L, + + 0x687F3584L, 0x52A0E286L, 0xB79C5305L, 0xAA500737L, + 0x3E07841CL, 0x7FDEAE5CL, 0x8E7D44ECL, 0x5716F2B8L, + 0xB03ADA37L, 0xF0500C0DL, 0xF01C1F04L, 0x0200B3FFL, + 0xAE0CF51AL, 0x3CB574B2L, 0x25837A58L, 0xDC0921BDL, + + 0xD19113F9L, 0x7CA92FF6L, 0x94324773L, 0x22F54701L, + 0x3AE5E581L, 0x37C2DADCL, 0xC8B57634L, 0x9AF3DDA7L, + 0xA9446146L, 0x0FD0030EL, 0xECC8C73EL, 0xA4751E41L, + 0xE238CD99L, 0x3BEA0E2FL, 0x3280BBA1L, 0x183EB331L, + + 0x4E548B38L, 0x4F6DB908L, 0x6F420D03L, 0xF60A04BFL, + 0x2CB81290L, 0x24977C79L, 0x5679B072L, 0xBCAF89AFL, + 0xDE9A771FL, 0xD9930810L, 0xB38BAE12L, 0xDCCF3F2EL, + 0x5512721FL, 0x2E6B7124L, 0x501ADDE6L, 0x9F84CD87L, + + 0x7A584718L, 0x7408DA17L, 0xBC9F9ABCL, 0xE94B7D8CL, + 0xEC7AEC3AL, 0xDB851DFAL, 0x63094366L, 0xC464C3D2L, + 0xEF1C1847L, 0x3215D908L, 0xDD433B37L, 0x24C2BA16L, + 0x12A14D43L, 0x2A65C451L, 0x50940002L, 0x133AE4DDL, + + 0x71DFF89EL, 0x10314E55L, 0x81AC77D6L, 0x5F11199BL, + 0x043556F1L, 0xD7A3C76BL, 0x3C11183BL, 0x5924A509L, + 0xF28FE6EDL, 0x97F1FBFAL, 0x9EBABF2CL, 0x1E153C6EL, + 0x86E34570L, 0xEAE96FB1L, 0x860E5E0AL, 0x5A3E2AB3L, + + 0x771FE71CL, 0x4E3D06FAL, 0x2965DCB9L, 0x99E71D0FL, + 0x803E89D6L, 0x5266C825L, 0x2E4CC978L, 0x9C10B36AL, + 0xC6150EBAL, 0x94E2EA78L, 0xA5FC3C53L, 0x1E0A2DF4L, + 0xF2F74EA7L, 0x361D2B3DL, 0x1939260FL, 0x19C27960L, + + 0x5223A708L, 0xF71312B6L, 0xEBADFE6EL, 0xEAC31F66L, + 0xE3BC4595L, 0xA67BC883L, 0xB17F37D1L, 0x018CFF28L, + 0xC332DDEFL, 0xBE6C5AA5L, 0x65582185L, 0x68AB9802L, + 0xEECEA50FL, 0xDB2F953BL, 0x2AEF7DADL, 0x5B6E2F84L, + + 0x1521B628L, 0x29076170L, 0xECDD4775L, 0x619F1510L, + 0x13CCA830L, 0xEB61BD96L, 0x0334FE1EL, 0xAA0363CFL, + 0xB5735C90L, 0x4C70A239L, 0xD59E9E0BL, 0xCBAADE14L, + 0xEECC86BCL, 0x60622CA7L, 0x9CAB5CABL, 0xB2F3846EL, + + 0x648B1EAFL, 0x19BDF0CAL, 0xA02369B9L, 0x655ABB50L, + 0x40685A32L, 0x3C2AB4B3L, 0x319EE9D5L, 0xC021B8F7L, + 0x9B540B19L, 0x875FA099L, 0x95F7997EL, 0x623D7DA8L, + 0xF837889AL, 0x97E32D77L, 0x11ED935FL, 0x16681281L, + + 0x0E358829L, 0xC7E61FD6L, 0x96DEDFA1L, 0x7858BA99L, + 0x57F584A5L, 0x1B227263L, 0x9B83C3FFL, 0x1AC24696L, + 0xCDB30AEBL, 0x532E3054L, 0x8FD948E4L, 0x6DBC3128L, + 0x58EBF2EFL, 0x34C6FFEAL, 0xFE28ED61L, 0xEE7C3C73L, + + 0x5D4A14D9L, 0xE864B7E3L, 0x42105D14L, 0x203E13E0L, + 0x45EEE2B6L, 0xA3AAABEAL, 0xDB6C4F15L, 0xFACB4FD0L, + 0xC742F442L, 0xEF6ABBB5L, 0x654F3B1DL, 0x41CD2105L, + 0xD81E799EL, 0x86854DC7L, 0xE44B476AL, 0x3D816250L, + + 0xCF62A1F2L, 0x5B8D2646L, 0xFC8883A0L, 0xC1C7B6A3L, + 0x7F1524C3L, 0x69CB7492L, 0x47848A0BL, 0x5692B285L, + 0x095BBF00L, 0xAD19489DL, 0x1462B174L, 0x23820E00L, + 0x58428D2AL, 0x0C55F5EAL, 0x1DADF43EL, 0x233F7061L, + + 0x3372F092L, 0x8D937E41L, 0xD65FECF1L, 0x6C223BDBL, + 0x7CDE3759L, 0xCBEE7460L, 0x4085F2A7L, 0xCE77326EL, + 0xA6078084L, 0x19F8509EL, 0xE8EFD855L, 0x61D99735L, + 0xA969A7AAL, 0xC50C06C2L, 0x5A04ABFCL, 0x800BCADCL, + + 0x9E447A2EL, 0xC3453484L, 0xFDD56705L, 0x0E1E9EC9L, + 0xDB73DBD3L, 0x105588CDL, 0x675FDA79L, 0xE3674340L, + 0xC5C43465L, 0x713E38D8L, 0x3D28F89EL, 0xF16DFF20L, + 0x153E21E7L, 0x8FB03D4AL, 0xE6E39F2BL, 0xDB83ADF7L + }, + + /* S[2] */ + { + 0xE93D5A68L, 0x948140F7L, 0xF64C261CL, 0x94692934L, + 0x411520F7L, 0x7602D4F7L, 0xBCF46B2EL, 0xD4A20068L, + 0xD4082471L, 0x3320F46AL, 0x43B7D4B7L, 0x500061AFL, + 0x1E39F62EL, 0x97244546L, 0x14214F74L, 0xBF8B8840L, + + 0x4D95FC1DL, 0x96B591AFL, 0x70F4DDD3L, 0x66A02F45L, + 0xBFBC09ECL, 0x03BD9785L, 0x7FAC6DD0L, 0x31CB8504L, + 0x96EB27B3L, 0x55FD3941L, 0xDA2547E6L, 0xABCA0A9AL, + 0x28507825L, 0x530429F4L, 0x0A2C86DAL, 0xE9B66DFBL, + + 0x68DC1462L, 0xD7486900L, 0x680EC0A4L, 0x27A18DEEL, + 0x4F3FFEA2L, 0xE887AD8CL, 0xB58CE006L, 0x7AF4D6B6L, + 0xAACE1E7CL, 0xD3375FECL, 0xCE78A399L, 0x406B2A42L, + 0x20FE9E35L, 0xD9F385B9L, 0xEE39D7ABL, 0x3B124E8BL, + + 0x1DC9FAF7L, 0x4B6D1856L, 0x26A36631L, 0xEAE397B2L, + 0x3A6EFA74L, 0xDD5B4332L, 0x6841E7F7L, 0xCA7820FBL, + 0xFB0AF54EL, 0xD8FEB397L, 0x454056ACL, 0xBA489527L, + 0x55533A3AL, 0x20838D87L, 0xFE6BA9B7L, 0xD096954BL, + + 0x55A867BCL, 0xA1159A58L, 0xCCA92963L, 0x99E1DB33L, + 0xA62A4A56L, 0x3F3125F9L, 0x5EF47E1CL, 0x9029317CL, + 0xFDF8E802L, 0x04272F70L, 0x80BB155CL, 0x05282CE3L, + 0x95C11548L, 0xE4C66D22L, 0x48C1133FL, 0xC70F86DCL, + + 0x07F9C9EEL, 0x41041F0FL, 0x404779A4L, 0x5D886E17L, + 0x325F51EBL, 0xD59BC0D1L, 0xF2BCC18FL, 0x41113564L, + 0x257B7834L, 0x602A9C60L, 0xDFF8E8A3L, 0x1F636C1BL, + 0x0E12B4C2L, 0x02E1329EL, 0xAF664FD1L, 0xCAD18115L, + + 0x6B2395E0L, 0x333E92E1L, 0x3B240B62L, 0xEEBEB922L, + 0x85B2A20EL, 0xE6BA0D99L, 0xDE720C8CL, 0x2DA2F728L, + 0xD0127845L, 0x95B794FDL, 0x647D0862L, 0xE7CCF5F0L, + 0x5449A36FL, 0x877D48FAL, 0xC39DFD27L, 0xF33E8D1EL, + + 0x0A476341L, 0x992EFF74L, 0x3A6F6EABL, 0xF4F8FD37L, + 0xA812DC60L, 0xA1EBDDF8L, 0x991BE14CL, 0xDB6E6B0DL, + 0xC67B5510L, 0x6D672C37L, 0x2765D43BL, 0xDCD0E804L, + 0xF1290DC7L, 0xCC00FFA3L, 0xB5390F92L, 0x690FED0BL, + + 0x667B9FFBL, 0xCEDB7D9CL, 0xA091CF0BL, 0xD9155EA3L, + 0xBB132F88L, 0x515BAD24L, 0x7B9479BFL, 0x763BD6EBL, + 0x37392EB3L, 0xCC115979L, 0x8026E297L, 0xF42E312DL, + 0x6842ADA7L, 0xC66A2B3BL, 0x12754CCCL, 0x782EF11CL, + + 0x6A124237L, 0xB79251E7L, 0x06A1BBE6L, 0x4BFB6350L, + 0x1A6B1018L, 0x11CAEDFAL, 0x3D25BDD8L, 0xE2E1C3C9L, + 0x44421659L, 0x0A121386L, 0xD90CEC6EL, 0xD5ABEA2AL, + 0x64AF674EL, 0xDA86A85FL, 0xBEBFE988L, 0x64E4C3FEL, + + 0x9DBC8057L, 0xF0F7C086L, 0x60787BF8L, 0x6003604DL, + 0xD1FD8346L, 0xF6381FB0L, 0x7745AE04L, 0xD736FCCCL, + 0x83426B33L, 0xF01EAB71L, 0xB0804187L, 0x3C005E5FL, + 0x77A057BEL, 0xBDE8AE24L, 0x55464299L, 0xBF582E61L, + + 0x4E58F48FL, 0xF2DDFDA2L, 0xF474EF38L, 0x8789BDC2L, + 0x5366F9C3L, 0xC8B38E74L, 0xB475F255L, 0x46FCD9B9L, + 0x7AEB2661L, 0x8B1DDF84L, 0x846A0E79L, 0x915F95E2L, + 0x466E598EL, 0x20B45770L, 0x8CD55591L, 0xC902DE4CL, + + 0xB90BACE1L, 0xBB8205D0L, 0x11A86248L, 0x7574A99EL, + 0xB77F19B6L, 0xE0A9DC09L, 0x662D09A1L, 0xC4324633L, + 0xE85A1F02L, 0x09F0BE8CL, 0x4A99A025L, 0x1D6EFE10L, + 0x1AB93D1DL, 0x0BA5A4DFL, 0xA186F20FL, 0x2868F169L, + + 0xDCB7DA83L, 0x573906FEL, 0xA1E2CE9BL, 0x4FCD7F52L, + 0x50115E01L, 0xA70683FAL, 0xA002B5C4L, 0x0DE6D027L, + 0x9AF88C27L, 0x773F8641L, 0xC3604C06L, 0x61A806B5L, + 0xF0177A28L, 0xC0F586E0L, 0x006058AAL, 0x30DC7D62L, + + 0x11E69ED7L, 0x2338EA63L, 0x53C2DD94L, 0xC2C21634L, + 0xBBCBEE56L, 0x90BCB6DEL, 0xEBFC7DA1L, 0xCE591D76L, + 0x6F05E409L, 0x4B7C0188L, 0x39720A3DL, 0x7C927C24L, + 0x86E3725FL, 0x724D9DB9L, 0x1AC15BB4L, 0xD39EB8FCL, + + 0xED545578L, 0x08FCA5B5L, 0xD83D7CD3L, 0x4DAD0FC4L, + 0x1E50EF5EL, 0xB161E6F8L, 0xA28514D9L, 0x6C51133CL, + 0x6FD5C7E7L, 0x56E14EC4L, 0x362ABFCEL, 0xDDC6C837L, + 0xD79A3234L, 0x92638212L, 0x670EFA8EL, 0x406000E0L + }, + + /* S[3] */ + { + 0x3A39CE37L, 0xD3FAF5CFL, 0xABC27737L, 0x5AC52D1BL, + 0x5CB0679EL, 0x4FA33742L, 0xD3822740L, 0x99BC9BBEL, + 0xD5118E9DL, 0xBF0F7315L, 0xD62D1C7EL, 0xC700C47BL, + 0xB78C1B6BL, 0x21A19045L, 0xB26EB1BEL, 0x6A366EB4L, + + 0x5748AB2FL, 0xBC946E79L, 0xC6A376D2L, 0x6549C2C8L, + 0x530FF8EEL, 0x468DDE7DL, 0xD5730A1DL, 0x4CD04DC6L, + 0x2939BBDBL, 0xA9BA4650L, 0xAC9526E8L, 0xBE5EE304L, + 0xA1FAD5F0L, 0x6A2D519AL, 0x63EF8CE2L, 0x9A86EE22L, + + 0xC089C2B8L, 0x43242EF6L, 0xA51E03AAL, 0x9CF2D0A4L, + 0x83C061BAL, 0x9BE96A4DL, 0x8FE51550L, 0xBA645BD6L, + 0x2826A2F9L, 0xA73A3AE1L, 0x4BA99586L, 0xEF5562E9L, + 0xC72FEFD3L, 0xF752F7DAL, 0x3F046F69L, 0x77FA0A59L, + + 0x80E4A915L, 0x87B08601L, 0x9B09E6ADL, 0x3B3EE593L, + 0xE990FD5AL, 0x9E34D797L, 0x2CF0B7D9L, 0x022B8B51L, + 0x96D5AC3AL, 0x017DA67DL, 0xD1CF3ED6L, 0x7C7D2D28L, + 0x1F9F25CFL, 0xADF2B89BL, 0x5AD6B472L, 0x5A88F54CL, + + 0xE029AC71L, 0xE019A5E6L, 0x47B0ACFDL, 0xED93FA9BL, + 0xE8D3C48DL, 0x283B57CCL, 0xF8D56629L, 0x79132E28L, + 0x785F0191L, 0xED756055L, 0xF7960E44L, 0xE3D35E8CL, + 0x15056DD4L, 0x88F46DBAL, 0x03A16125L, 0x0564F0BDL, + + 0xC3EB9E15L, 0x3C9057A2L, 0x97271AECL, 0xA93A072AL, + 0x1B3F6D9BL, 0x1E6321F5L, 0xF59C66FBL, 0x26DCF319L, + 0x7533D928L, 0xB155FDF5L, 0x03563482L, 0x8ABA3CBBL, + 0x28517711L, 0xC20AD9F8L, 0xABCC5167L, 0xCCAD925FL, + + 0x4DE81751L, 0x3830DC8EL, 0x379D5862L, 0x9320F991L, + 0xEA7A90C2L, 0xFB3E7BCEL, 0x5121CE64L, 0x774FBE32L, + 0xA8B6E37EL, 0xC3293D46L, 0x48DE5369L, 0x6413E680L, + 0xA2AE0810L, 0xDD6DB224L, 0x69852DFDL, 0x09072166L, + + 0xB39A460AL, 0x6445C0DDL, 0x586CDECFL, 0x1C20C8AEL, + 0x5BBEF7DDL, 0x1B588D40L, 0xCCD2017FL, 0x6BB4E3BBL, + 0xDDA26A7EL, 0x3A59FF45L, 0x3E350A44L, 0xBCB4CDD5L, + 0x72EACEA8L, 0xFA6484BBL, 0x8D6612AEL, 0xBF3C6F47L, + + 0xD29BE463L, 0x542F5D9EL, 0xAEC2771BL, 0xF64E6370L, + 0x740E0D8DL, 0xE75B1357L, 0xF8721671L, 0xAF537D5DL, + 0x4040CB08L, 0x4EB4E2CCL, 0x34D2466AL, 0x0115AF84L, + 0xE1B00428L, 0x95983A1DL, 0x06B89FB4L, 0xCE6EA048L, + + 0x6F3F3B82L, 0x3520AB82L, 0x011A1D4BL, 0x277227F8L, + 0x611560B1L, 0xE7933FDCL, 0xBB3A792BL, 0x344525BDL, + 0xA08839E1L, 0x51CE794BL, 0x2F32C9B7L, 0xA01FBAC9L, + 0xE01CC87EL, 0xBCC7D1F6L, 0xCF0111C3L, 0xA1E8AAC7L, + + 0x1A908749L, 0xD44FBD9AL, 0xD0DADECBL, 0xD50ADA38L, + 0x0339C32AL, 0xC6913667L, 0x8DF9317CL, 0xE0B12B4FL, + 0xF79E59B7L, 0x43F5BB3AL, 0xF2D519FFL, 0x27D9459CL, + 0xBF97222CL, 0x15E6FC2AL, 0x0F91FC71L, 0x9B941525L, + + 0xFAE59361L, 0xCEB69CEBL, 0xC2A86459L, 0x12BAA8D1L, + 0xB6C1075EL, 0xE3056A0CL, 0x10D25065L, 0xCB03A442L, + 0xE0EC6E0EL, 0x1698DB3BL, 0x4C98A0BEL, 0x3278E964L, + 0x9F1F9532L, 0xE0D392DFL, 0xD3A0342BL, 0x8971F21EL, + + 0x1B0A7441L, 0x4BA3348CL, 0xC5BE7120L, 0xC37632D8L, + 0xDF359F8DL, 0x9B992F2EL, 0xE60B6F47L, 0x0FE3F11DL, + 0xE54CDA54L, 0x1EDAD891L, 0xCE6279CFL, 0xCD3E7E6FL, + 0x1618B166L, 0xFD2C1D05L, 0x848FD2C5L, 0xF6FB2299L, + + 0xF523F357L, 0xA6327623L, 0x93A83531L, 0x56CCCD02L, + 0xACF08162L, 0x5A75EBB5L, 0x6E163697L, 0x88D273CCL, + 0xDE966292L, 0x81B949D0L, 0x4C50901BL, 0x71C65614L, + 0xE6C6C7BDL, 0x327A140AL, 0x45E1D006L, 0xC3F27B9AL, + + 0xC9AA53FDL, 0x62A80F00L, 0xBB25BFE2L, 0x35BDD2F6L, + 0x71126905L, 0xB2040222L, 0xB6CBCF7CL, 0xCD769C2BL, + 0x53113EC0L, 0x1640E3D3L, 0x38ABBD60L, 0x2547ADF0L, + 0xBA38209CL, 0xF746CE76L, 0x77AFA1C5L, 0x20756060L, + + 0x85CBFE4EL, 0x8AE88DD8L, 0x7AAAF9B0L, 0x4CF9AA7EL, + 0x1948C25CL, 0x02FB8A8CL, 0x01C36AE4L, 0xD6EBE1F9L, + 0x90D4F869L, 0xA65CDEA0L, 0x3F09252DL, 0xC208E69FL, + 0xB74E6132L, 0xCE77E25BL, 0x578FDFE3L, 0x3AC372E6L + } + }, + + /* P */ + { + 0x243F6A88L, 0x85A308D3L, 0x13198A2EL, 0x03707344L, + 0xA4093822L, 0x299F31D0L, 0x082EFA98L, 0xEC4E6C89L, + 0x452821E6L, 0x38D01377L, 0xBE5466CFL, 0x34E90C6CL, + 0xC0AC29B7L, 0xC97C50DDL, 0x3F84D5B5L, 0xB5470917L, + 0x9216D5D9L, 0x8979FB1BL + } +}; +#endif + +#if !defined LIBO_CIPHER_OPENSSL_BACKEND +static rtlCipherError BF_init( + CipherContextBF *ctx, + rtlCipherMode eMode, + const sal_uInt8 *pKeyData, sal_Size nKeyLen, + const sal_uInt8 *pArgData, sal_Size nArgLen) +{ + CipherKeyBF *key; + sal_uInt32 D, DL, DR; + sal_uInt16 i, j, k; + + key = &(ctx->m_key); + + memcpy(key, &BF_key, sizeof (CipherKeyBF)); + memset(&(ctx->m_iv), 0, sizeof(ctx->m_iv)); + ctx->m_offset = 0; + + for (i = 0, k = 0; i < CIPHER_ROUNDS_BF + 2; ++i) + { + D = 0; + for (j = 0; j < 4; ++j) + { + D = ((D << 8) | pKeyData[k]); + k++; + if (k >= nKeyLen) + k = 0; + } + key->m_P[i] ^= D; + } + + rtl_secureZeroMemory(&DL, sizeof(DL)); + rtl_secureZeroMemory(&DR, sizeof(DR)); + + for (i = 0; i < CIPHER_ROUNDS_BF + 2; i += 2) + { + BF_encode(key, &DL, &DR); + key->m_P[i ] = DL; + key->m_P[i + 1] = DR; + } + + for (i = 0; i < 4; ++i) + { + for (k = 0; k < 256; k += 2) + { + BF_encode(key, &DL, &DR); + key->m_S[i][k ] = DL; + key->m_S[i][k + 1] = DR; + } + } + + if (pArgData && nArgLen) + { + nArgLen = std::min<sal_Size>(nArgLen, 8); + if (eMode == rtl_Cipher_ModeStream) + { + memcpy(ctx->m_iv.m_byte, pArgData, nArgLen); + } + else + { + RTL_CIPHER_NTOHL64 (pArgData, DL, DR, nArgLen); + ctx->m_iv.m_long[0] = DL; + ctx->m_iv.m_long[1] = DR; + } + } + + return rtl_Cipher_E_None; +} +#endif + +static rtlCipherError BF_update( + CipherContextBF *ctx, + rtlCipherMode eMode, + rtlCipherDirection eDirection, + const sal_uInt8 *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen) +{ + /* Check arguments. */ + if (!pData || !pBuffer) + return rtl_Cipher_E_Argument; + + if ((nDatLen <= 0) || (nDatLen > nBufLen)) + return rtl_Cipher_E_BufferSize; + + /* Update. */ +#if defined LIBO_CIPHER_OPENSSL_BACKEND + assert(eMode == rtl_Cipher_ModeStream); + (void) eMode; + (void) eDirection; + while (nDatLen > o3tl::make_unsigned(std::numeric_limits<int>::max())) { + int outl; + if (EVP_CipherUpdate(ctx->m_context, pBuffer, &outl, pData, std::numeric_limits<int>::max()) + == 0) + { + return rtl_Cipher_E_Unknown; + } + assert(outl == std::numeric_limits<int>::max()); + pData += std::numeric_limits<int>::max(); + nDatLen -= std::numeric_limits<int>::max(); + pBuffer += std::numeric_limits<int>::max(); + } + int outl; + if (EVP_CipherUpdate(ctx->m_context, pBuffer, &outl, pData, static_cast<int>(nDatLen)) == 0) + { + return rtl_Cipher_E_Unknown; + } + assert(outl == static_cast<int>(nDatLen)); + // A final call to EVP_CipherFinal_ex is intentionally missing; it wouldn't fit the rtl/cipher.h + // interface, and is hopefully not needed, as each individual Blowfish CFB update step doesn't + // hold back any data that would need to be finally flushed. +#else + if (eMode == rtl_Cipher_ModeECB) + { + /* Block mode. */ + while (nDatLen > 8) + { + BF_updateECB(ctx, eDirection, pData, pBuffer, 8); + nDatLen -= 8; + pData += 8; + pBuffer += 8; + } + BF_updateECB(ctx, eDirection, pData, pBuffer, nDatLen); + } + else if (eMode == rtl_Cipher_ModeCBC) + { + /* Block mode. */ + while (nDatLen > 8) + { + BF_updateCBC (ctx, eDirection, pData, pBuffer, 8); + nDatLen -= 8; + pData += 8; + pBuffer += 8; + } + BF_updateCBC (ctx, eDirection, pData, pBuffer, nDatLen); + } + else + { + /* Stream mode. */ + while (nDatLen > 0) + { + BF_updateCFB (ctx, eDirection, pData, pBuffer); + nDatLen -= 1; + pData += 1; + pBuffer += 1; + } + } +#endif + return rtl_Cipher_E_None; +} + +#if !defined LIBO_CIPHER_OPENSSL_BACKEND +static void BF_updateECB( + CipherContextBF *ctx, + rtlCipherDirection direction, + const sal_uInt8 *pData, + sal_uInt8 *pBuffer, + sal_Size nLength) +{ + CipherKeyBF *key; + sal_uInt32 DL, DR; + + key = &(ctx->m_key); + if (direction == rtl_Cipher_DirectionEncode) + { + RTL_CIPHER_NTOHL64(pData, DL, DR, nLength); + + BF_encode(key, &DL, &DR); + + RTL_CIPHER_HTONL(DL, pBuffer); + RTL_CIPHER_HTONL(DR, pBuffer); + } + else + { + RTL_CIPHER_NTOHL(pData, DL); + RTL_CIPHER_NTOHL(pData, DR); + + BF_decode(key, &DL, &DR); + + RTL_CIPHER_HTONL64(DL, DR, pBuffer, nLength); + } + rtl_secureZeroMemory(&DL, sizeof(DL)); + rtl_secureZeroMemory(&DR, sizeof(DR)); +} + +static void BF_updateCBC( + CipherContextBF *ctx, + rtlCipherDirection direction, + const sal_uInt8 *pData, + sal_uInt8 *pBuffer, + sal_Size nLength) +{ + CipherKeyBF *key; + sal_uInt32 DL, DR; + + key = &(ctx->m_key); + if (direction == rtl_Cipher_DirectionEncode) + { + RTL_CIPHER_NTOHL64(pData, DL, DR, nLength); + + DL ^= ctx->m_iv.m_long[0]; + DR ^= ctx->m_iv.m_long[1]; + + BF_encode(key, &DL, &DR); + + ctx->m_iv.m_long[0] = DL; + ctx->m_iv.m_long[1] = DR; + + RTL_CIPHER_HTONL(DL, pBuffer); + RTL_CIPHER_HTONL(DR, pBuffer); + } + else + { + sal_uInt32 IVL, IVR; + + RTL_CIPHER_NTOHL(pData, DL); + RTL_CIPHER_NTOHL(pData, DR); + + IVL = DL; + IVR = DR; + + BF_decode(key, &DL, &DR); + + DL ^= ctx->m_iv.m_long[0]; + DR ^= ctx->m_iv.m_long[1]; + + ctx->m_iv.m_long[0] = IVL; + ctx->m_iv.m_long[1] = IVR; + + RTL_CIPHER_HTONL64(DL, DR, pBuffer, nLength); + } + rtl_secureZeroMemory(&DL, sizeof(DL)); + rtl_secureZeroMemory(&DR, sizeof(DR)); +} + +static void BF_updateCFB( + CipherContextBF *ctx, + rtlCipherDirection direction, + const sal_uInt8 *pData, + sal_uInt8 *pBuffer) +{ + sal_uInt8 *iv; + sal_uInt32 k; + + iv = ctx->m_iv.m_byte; + k = ctx->m_offset; + + if (k == 0) + { + sal_uInt32 IVL, IVR; + + RTL_CIPHER_NTOHL64(iv, IVL, IVR, 8); + BF_encode(&(ctx->m_key), &IVL, &IVR); + RTL_CIPHER_HTONL64(IVL, IVR, iv, 8); + + rtl_secureZeroMemory(&IVL, sizeof(IVL)); + rtl_secureZeroMemory(&IVR, sizeof(IVR)); + } + + if (direction == rtl_Cipher_DirectionEncode) + { + iv[k] ^= *pData; + *pBuffer = iv[k]; + } + else + { + sal_uInt8 c = iv[k]; + iv[k] = *pData; + *pBuffer = *pData ^ c; + } + + ctx->m_offset = ((k + 1) & 0x07); + iv = nullptr; +} + +static void BF_encode( + CipherKeyBF *key, sal_uInt32 *xl, sal_uInt32 *xr) +{ + sal_uInt32 t, XL, XR; + sal_uInt16 i; + + XL = *xl; + XR = *xr; + + for (i = 0; i < CIPHER_ROUNDS_BF; ++i) + { + XL ^= key->m_P[i]; + XR ^= BF (key, XL); + + t = XL; + XL = XR; + XR = t; + } + + t = XL; + XL = XR; + XR = t; + + XR ^= key->m_P[CIPHER_ROUNDS_BF ]; + XL ^= key->m_P[CIPHER_ROUNDS_BF + 1]; + + *xl = XL; + *xr = XR; + +} + +static void BF_decode( + CipherKeyBF *key, sal_uInt32 *xl, sal_uInt32 *xr) +{ + sal_uInt32 t, XL, XR; + sal_uInt16 i; + + XL = *xl; + XR = *xr; + + for (i = CIPHER_ROUNDS_BF + 1; i > 1; --i) + { + XL ^= key->m_P[i]; + XR ^= BF (key, XL); + + t = XL; + XL = XR; + XR = t; + } + + t = XL; + XL = XR; + XR = t; + + XR ^= key->m_P[1]; + XL ^= key->m_P[0]; + + *xl = XL; + *xr = XR; + +} + +static sal_uInt32 BF(CipherKeyBF *key, sal_uInt32 x) +{ + sal_uInt16 a, b, c, d; + sal_uInt32 y; + + d = static_cast<sal_uInt16>(x & 0x00ff); + x >>= 8; + c = static_cast<sal_uInt16>(x & 0x00ff); + x >>= 8; + b = static_cast<sal_uInt16>(x & 0x00ff); + x >>= 8; + a = static_cast<sal_uInt16>(x & 0x00ff); + + y = key->m_S[0][a]; + y += key->m_S[1][b]; + y ^= key->m_S[2][c]; + y += key->m_S[3][d]; + + return y; +} +#endif + +/** + rtl_cipherBF (Blowfish) implementation. + + Reference: Bruce Schneier: Applied Cryptography, 2nd edition, ch. 14.3 +*/ +rtlCipher SAL_CALL rtl_cipher_createBF(rtlCipherMode Mode) SAL_THROW_EXTERN_C() +{ + CipherBF_Impl *pImpl = nullptr; + + if (Mode == rtl_Cipher_ModeInvalid) + return nullptr; +#if defined LIBO_CIPHER_OPENSSL_BACKEND + if (Mode != rtl_Cipher_ModeStream) { + // Cannot easily support ModeECB and ModeCBC, and they aren't used in the LO code at least: + return nullptr; + } +#endif + + pImpl = static_cast<CipherBF_Impl*>(rtl_allocateZeroMemory(sizeof (CipherBF_Impl))); + if (pImpl) + { + pImpl->m_cipher.m_algorithm = rtl_Cipher_AlgorithmBF; + pImpl->m_cipher.m_direction = rtl_Cipher_DirectionInvalid; + pImpl->m_cipher.m_mode = Mode; + + pImpl->m_cipher.m_init = rtl_cipher_initBF; + pImpl->m_cipher.m_encode = rtl_cipher_encodeBF; + pImpl->m_cipher.m_decode = rtl_cipher_decodeBF; + pImpl->m_cipher.m_delete = rtl_cipher_destroyBF; + } + return static_cast<rtlCipher>(pImpl); +} + +rtlCipherError SAL_CALL rtl_cipher_initBF( + rtlCipher Cipher, + rtlCipherDirection Direction, + const sal_uInt8 *pKeyData, sal_Size nKeyLen, + const sal_uInt8 *pArgData, sal_Size nArgLen) SAL_THROW_EXTERN_C() +{ + CipherBF_Impl *pImpl = static_cast<CipherBF_Impl*>(Cipher); + + if (!pImpl || !pKeyData) + return rtl_Cipher_E_Argument; + + if (pImpl->m_cipher.m_algorithm != rtl_Cipher_AlgorithmBF) + return rtl_Cipher_E_Algorithm; + + if (Direction != rtl_Cipher_DirectionInvalid) + pImpl->m_cipher.m_direction = Direction; + else + return rtl_Cipher_E_Direction; + +#if defined LIBO_CIPHER_OPENSSL_BACKEND + if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionBoth) { + // Cannot easily support DirectionBoth, and it isn't used in the LO code at least: + return rtl_Cipher_E_Direction; + } + if (nKeyLen > o3tl::make_unsigned(std::numeric_limits<int>::max())) { + return rtl_Cipher_E_BufferSize; + } + if (pImpl->m_context.m_context != nullptr) { + EVP_CIPHER_CTX_free(pImpl->m_context.m_context); + } + pImpl->m_context.m_context = EVP_CIPHER_CTX_new(); + if (pImpl->m_context.m_context == nullptr) { + return rtl_Cipher_E_Memory; + } + unsigned char iv[8]; + auto const n = std::min(nArgLen, sal_Size(8)); + std::memcpy(iv, pArgData, n); + std::memset(iv + n, 0, 8 - n); + if (EVP_CipherInit_ex( + pImpl->m_context.m_context, EVP_bf_cfb(), nullptr, nullptr, iv, + pImpl->m_cipher.m_direction == rtl_Cipher_DirectionDecode ? 0 : 1) + == 0) + { + return rtl_Cipher_E_Unknown; + } + if (EVP_CIPHER_CTX_set_key_length(pImpl->m_context.m_context, static_cast<int>(nKeyLen)) == 0) { + return rtl_Cipher_E_Unknown; + } + if (EVP_CipherInit_ex(pImpl->m_context.m_context, nullptr, nullptr, pKeyData, nullptr, -1) == 0) + { + return rtl_Cipher_E_Unknown; + } + return rtl_Cipher_E_None; +#else + return BF_init( + &(pImpl->m_context), pImpl->m_cipher.m_mode, + pKeyData, nKeyLen, pArgData, nArgLen); +#endif +} + +rtlCipherError SAL_CALL rtl_cipher_encodeBF( + rtlCipher Cipher, + const void *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen) SAL_THROW_EXTERN_C() +{ + CipherBF_Impl *pImpl = static_cast<CipherBF_Impl*>(Cipher); + if (!pImpl) + return rtl_Cipher_E_Argument; + + if (pImpl->m_cipher.m_algorithm != rtl_Cipher_AlgorithmBF) + return rtl_Cipher_E_Algorithm; + + if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionInvalid) + return rtl_Cipher_E_Direction; + + if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionDecode) + return rtl_Cipher_E_Direction; + + return BF_update( + &(pImpl->m_context), pImpl->m_cipher.m_mode, + rtl_Cipher_DirectionEncode, + static_cast<const sal_uInt8*>(pData), nDatLen, pBuffer, nBufLen); +} + +rtlCipherError SAL_CALL rtl_cipher_decodeBF( + rtlCipher Cipher, + const void *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen) SAL_THROW_EXTERN_C() +{ + CipherBF_Impl *pImpl = static_cast<CipherBF_Impl*>(Cipher); + if (!pImpl) + return rtl_Cipher_E_Argument; + + if (pImpl->m_cipher.m_algorithm != rtl_Cipher_AlgorithmBF) + return rtl_Cipher_E_Algorithm; + + if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionInvalid) + return rtl_Cipher_E_Direction; + + if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionEncode) + return rtl_Cipher_E_Direction; + + return BF_update( + &(pImpl->m_context), pImpl->m_cipher.m_mode, + rtl_Cipher_DirectionDecode, + static_cast<const sal_uInt8*>(pData), nDatLen, pBuffer, nBufLen); +} + +void SAL_CALL rtl_cipher_destroyBF(rtlCipher Cipher) SAL_THROW_EXTERN_C() +{ + CipherBF_Impl *pImpl = static_cast<CipherBF_Impl*>(Cipher); + if (!pImpl) + return; + + if (pImpl->m_cipher.m_algorithm == rtl_Cipher_AlgorithmBF) + { +#if defined LIBO_CIPHER_OPENSSL_BACKEND + if (pImpl->m_context.m_context != nullptr) { + EVP_CIPHER_CTX_free(pImpl->m_context.m_context); + } +#endif + rtl_freeZeroMemory(pImpl, sizeof(CipherBF_Impl)); + } + else + free(pImpl); +} + +#if !defined LIBO_CIPHER_OPENSSL_BACKEND +#define CIPHER_CBLOCK_ARCFOUR 256 +#endif + +namespace { + +struct ContextARCFOUR_Impl +{ +#if defined LIBO_CIPHER_OPENSSL_BACKEND + EVP_CIPHER_CTX * m_context; +#else + unsigned int m_S[CIPHER_CBLOCK_ARCFOUR]; + unsigned int m_X, m_Y; +#endif +}; + +struct CipherARCFOUR_Impl +{ + Cipher_Impl m_cipher; + ContextARCFOUR_Impl m_context; +}; + +} + +static rtlCipherError rtl_cipherARCFOUR_update_Impl( + ContextARCFOUR_Impl *ctx, + const sal_uInt8 *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen); + +static rtlCipherError rtl_cipherARCFOUR_init_Impl( + ContextARCFOUR_Impl *ctx, + const sal_uInt8 *pKeyData, sal_Size nKeyLen) +{ +#if defined LIBO_CIPHER_OPENSSL_BACKEND + if (nKeyLen > o3tl::make_unsigned(std::numeric_limits<int>::max())) { + return rtl_Cipher_E_BufferSize; + } + if (ctx->m_context != nullptr) { + EVP_CIPHER_CTX_free(ctx->m_context); + } + ctx->m_context = EVP_CIPHER_CTX_new(); + if (ctx->m_context == nullptr) { + return rtl_Cipher_E_Memory; + } + if (EVP_CipherInit_ex(ctx->m_context, EVP_rc4(), nullptr, nullptr, nullptr, 0) == 0) { + // RC4 en- and decryption is identical, so we can use 0=decrypt regardless of direction, + // and thus also support rtl_Cipher_DirectionBoth + return rtl_Cipher_E_Unknown; + } + if (EVP_CIPHER_CTX_set_key_length(ctx->m_context, static_cast<int>(nKeyLen)) == 0) { + return rtl_Cipher_E_Unknown; + } + if (EVP_CipherInit_ex(ctx->m_context, nullptr, nullptr, pKeyData, nullptr, -1) == 0) { + return rtl_Cipher_E_Unknown; + } +#else + unsigned int K[CIPHER_CBLOCK_ARCFOUR]; + unsigned int *L, *S; + unsigned int x, y; + sal_Size n, k; + + S = &(ctx->m_S[0]); + + /* Initialize S linearly. */ + for (x = 0; x < CIPHER_CBLOCK_ARCFOUR; x++) + S[x] = x; + + /* Initialize K with key, repeat key as necessary. */ + for (L = K, n = CIPHER_CBLOCK_ARCFOUR; n > nKeyLen; n -= nKeyLen) + { + for (k = 0; k < nKeyLen; k++) + { + L[k] = pKeyData[k]; + } + + L += nKeyLen; + } + + for (k = 0; k < n; k++) + { + L[k] = pKeyData[k]; + } + + /* Initialize S with K. */ + for (x = 0, y = 0; x < CIPHER_CBLOCK_ARCFOUR; x++) + { + y = (y + S[x] + K[x]) % CIPHER_CBLOCK_ARCFOUR; + /* swap S[x] and S[y] */ + unsigned int t = S[x]; + S[x] = S[y]; + S[y] = t; + } + + /* Initialize counters X and Y. */ + ctx->m_X = 0; + ctx->m_Y = 0; +#endif + + return rtl_Cipher_E_None; +} + +static rtlCipherError rtl_cipherARCFOUR_update_Impl( + ContextARCFOUR_Impl *ctx, + const sal_uInt8 *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen) +{ + /* Check arguments. */ + if (!pData || !pBuffer) + return rtl_Cipher_E_Argument; + + if ((0 >= nDatLen) || (nDatLen > nBufLen)) + return rtl_Cipher_E_BufferSize; + +#if defined LIBO_CIPHER_OPENSSL_BACKEND + while (nDatLen > o3tl::make_unsigned(std::numeric_limits<int>::max())) { + int outl; + if (EVP_CipherUpdate(ctx->m_context, pBuffer, &outl, pData, std::numeric_limits<int>::max()) + == 0) + { + return rtl_Cipher_E_Unknown; + } + assert(outl == std::numeric_limits<int>::max()); + pData += std::numeric_limits<int>::max(); + nDatLen -= std::numeric_limits<int>::max(); + pBuffer += std::numeric_limits<int>::max(); + } + int outl; + if (EVP_CipherUpdate(ctx->m_context, pBuffer, &outl, pData, static_cast<int>(nDatLen)) == 0) { + return rtl_Cipher_E_Unknown; + } + assert(outl == static_cast<int>(nDatLen)); + // A final call to EVP_CipherFinal_ex is intentionally missing; it wouldn't fit the rtl/cipher.h + // interface, and is hopefully not needed, as each individual RC4 update step doesn't hold back + // any data that would need to be finally flushed. +#else + unsigned int *S; + sal_Size k; + + /* Update. */ + S = &(ctx->m_S[0]); + for (k = 0; k < nDatLen; k++) + { + /* Update counters X and Y. */ + unsigned int x = ctx->m_X; + unsigned int y = ctx->m_Y; + x = (x + 1 ) % CIPHER_CBLOCK_ARCFOUR; + y = (y + S[x]) % CIPHER_CBLOCK_ARCFOUR; + ctx->m_X = x; + ctx->m_Y = y; + + /* Swap S[x] and S[y]. */ + unsigned int t = S[x]; + S[x] = S[y]; + S[y] = t; + + /* Evaluate next key byte S[t]. */ + t = (S[x] + S[y]) % CIPHER_CBLOCK_ARCFOUR; + pBuffer[k] = pData[k] ^ static_cast<sal_uInt8>(S[t] & 0xff); + } +#endif + + return rtl_Cipher_E_None; +} + +/** + rtl_cipher_ARCFOUR (RC4) implementation. + + Reference: Bruce Schneier: Applied Cryptography, 2nd edition, ch. 17.1 +*/ +rtlCipher SAL_CALL rtl_cipher_createARCFOUR(rtlCipherMode Mode) + SAL_THROW_EXTERN_C() +{ + CipherARCFOUR_Impl *pImpl = nullptr; + + if (Mode != rtl_Cipher_ModeStream) + return nullptr; + + pImpl = static_cast<CipherARCFOUR_Impl*>(rtl_allocateZeroMemory(sizeof(CipherARCFOUR_Impl))); + if (pImpl) + { + pImpl->m_cipher.m_algorithm = rtl_Cipher_AlgorithmARCFOUR; + pImpl->m_cipher.m_direction = rtl_Cipher_DirectionInvalid; + pImpl->m_cipher.m_mode = rtl_Cipher_ModeStream; + + pImpl->m_cipher.m_init = rtl_cipher_initARCFOUR; + pImpl->m_cipher.m_encode = rtl_cipher_encodeARCFOUR; + pImpl->m_cipher.m_decode = rtl_cipher_decodeARCFOUR; + pImpl->m_cipher.m_delete = rtl_cipher_destroyARCFOUR; + } + + return static_cast<rtlCipher>(pImpl); +} + +rtlCipherError SAL_CALL rtl_cipher_initARCFOUR( + rtlCipher Cipher, + rtlCipherDirection Direction, + const sal_uInt8 *pKeyData, sal_Size nKeyLen, + SAL_UNUSED_PARAMETER const sal_uInt8 *, SAL_UNUSED_PARAMETER sal_Size) + SAL_THROW_EXTERN_C() +{ + CipherARCFOUR_Impl *pImpl = static_cast<CipherARCFOUR_Impl*>(Cipher); + + if (!pImpl || !pKeyData) + return rtl_Cipher_E_Argument; + + if (pImpl->m_cipher.m_algorithm != rtl_Cipher_AlgorithmARCFOUR) + return rtl_Cipher_E_Algorithm; + + if (Direction != rtl_Cipher_DirectionInvalid) + pImpl->m_cipher.m_direction = Direction; + else + return rtl_Cipher_E_Direction; + + return rtl_cipherARCFOUR_init_Impl(&(pImpl->m_context), pKeyData, nKeyLen); +} + +rtlCipherError SAL_CALL rtl_cipher_encodeARCFOUR( + rtlCipher Cipher, + const void *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen) SAL_THROW_EXTERN_C() +{ + CipherARCFOUR_Impl *pImpl = static_cast<CipherARCFOUR_Impl*>(Cipher); + if (!pImpl) + return rtl_Cipher_E_Argument; + + if (pImpl->m_cipher.m_algorithm != rtl_Cipher_AlgorithmARCFOUR) + return rtl_Cipher_E_Algorithm; + + if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionInvalid) + return rtl_Cipher_E_Direction; + + return rtl_cipherARCFOUR_update_Impl( + &(pImpl->m_context), + static_cast<const sal_uInt8*>(pData), nDatLen, pBuffer, nBufLen); +} + +rtlCipherError SAL_CALL rtl_cipher_decodeARCFOUR( + rtlCipher Cipher, + const void *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen) SAL_THROW_EXTERN_C() +{ + CipherARCFOUR_Impl *pImpl = static_cast<CipherARCFOUR_Impl*>(Cipher); + if (!pImpl) + return rtl_Cipher_E_Argument; + + if (pImpl->m_cipher.m_algorithm != rtl_Cipher_AlgorithmARCFOUR) + return rtl_Cipher_E_Algorithm; + + if (pImpl->m_cipher.m_direction == rtl_Cipher_DirectionInvalid) + return rtl_Cipher_E_Direction; + + return rtl_cipherARCFOUR_update_Impl( + &(pImpl->m_context), + static_cast<const sal_uInt8*>(pData), nDatLen, pBuffer, nBufLen); +} + +void SAL_CALL rtl_cipher_destroyARCFOUR(rtlCipher Cipher) SAL_THROW_EXTERN_C() +{ + CipherARCFOUR_Impl *pImpl = static_cast<CipherARCFOUR_Impl*>(Cipher); + if (!pImpl) + return; + + if (pImpl->m_cipher.m_algorithm == rtl_Cipher_AlgorithmARCFOUR) + { +#if defined LIBO_CIPHER_OPENSSL_BACKEND + if (pImpl->m_context.m_context != nullptr) { + EVP_CIPHER_CTX_free(pImpl->m_context.m_context); + } +#endif + rtl_freeZeroMemory(pImpl, sizeof(CipherARCFOUR_Impl)); + } + else + free(pImpl); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/cmdargs.cxx b/sal/rtl/cmdargs.cxx new file mode 100644 index 000000000..bad833077 --- /dev/null +++ b/sal/rtl/cmdargs.cxx @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <osl/mutex.hxx> +#include <rtl/alloc.h> +#include <rtl/process.h> +#include <rtl/ustring.hxx> + +namespace { + +rtl_uString ** g_ppCommandArgs = nullptr; +sal_uInt32 g_nCommandArgCount = 0; + +struct ArgHolder +{ + ~ArgHolder(); +}; + +ArgHolder::~ArgHolder() +{ + while (g_nCommandArgCount > 0) + rtl_uString_release (g_ppCommandArgs[--g_nCommandArgCount]); + + free (g_ppCommandArgs); + g_ppCommandArgs = nullptr; +} + +// The destructor of this static ArgHolder is "activated" by the assignments to +// g_ppCommandArgs and g_nCommandArgCount in init(): +ArgHolder argHolder; + +void init() +{ + osl::MutexGuard guard( osl::Mutex::getGlobalMutex() ); + if (g_ppCommandArgs) + return; + + sal_Int32 i, n = osl_getCommandArgCount(); + + g_ppCommandArgs = + static_cast<rtl_uString**>(rtl_allocateZeroMemory (n * sizeof(rtl_uString*))); + for (i = 0; i < n; i++) + { + rtl_uString * pArg = nullptr; + osl_getCommandArg (i, &pArg); + bool env; + { + auto const & arg = OUString::unacquired(&pArg); + env = (arg.startsWith("-") || arg.startsWith("/")) && + arg.match("env:", 1) && + arg.indexOf ('=') >= 0; + } + if (env ) + { + // ignore. + rtl_uString_release (pArg); + } + else + { + // assign. + g_ppCommandArgs[g_nCommandArgCount++] = pArg; + } + } +} + +} + +oslProcessError SAL_CALL rtl_getAppCommandArg ( + sal_uInt32 nArg, rtl_uString **ppCommandArg) +{ + init(); + oslProcessError result = osl_Process_E_NotFound; + if( nArg < g_nCommandArgCount ) + { + rtl_uString_assign( ppCommandArg, g_ppCommandArgs[nArg] ); + result = osl_Process_E_None; + } + return result; +} + +sal_uInt32 SAL_CALL rtl_getAppCommandArgCount() +{ + init(); + return g_nCommandArgCount; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/crc.cxx b/sal/rtl/crc.cxx new file mode 100644 index 000000000..7912b15d0 --- /dev/null +++ b/sal/rtl/crc.cxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/types.h> +#include <rtl/crc.h> +#include <cassert> + +#include <zlib.h> + +// Zlib's crc32() is very fast, so simply use that one instead +// of implementing this ourselves. + +sal_uInt32 SAL_CALL rtl_crc32 ( + sal_uInt32 Crc, + const void *Data, sal_uInt32 DatLen) SAL_THROW_EXTERN_C() +{ + // Check that our types map to zlib correctly. + static_assert(sizeof(uLong) >= sizeof(sal_uInt32)); + static_assert(sizeof(uInt) >= sizeof(sal_uInt32)); + // Our API says that Crc should be initialized to 0, while + // zlib says it should be initialized with 'crc32(0,Z_NULL,0)', + // which however simply returns 0. Ensure this. + assert(crc32(0, Z_NULL, 0) == 0); + if (Data) + Crc = crc32( Crc, static_cast<const Bytef*>(Data), DatLen); + return Crc; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/digest.cxx b/sal/rtl/digest.cxx new file mode 100644 index 000000000..de52ecae5 --- /dev/null +++ b/sal/rtl/digest.cxx @@ -0,0 +1,1897 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <string.h> +#include <stdlib.h> + +#include <sal/types.h> +#include <osl/endian.h> +#include <rtl/alloc.h> +#include <rtl/digest.h> + +#define RTL_DIGEST_CREATE(T) (static_cast<T*>(rtl_allocateZeroMemory(sizeof(T)))) + +#define RTL_DIGEST_ROTL(a,n) (((a) << (n)) | ((a) >> (32 - (n)))) + +#define RTL_DIGEST_HTONL(l,c) \ + (*((c)++) = static_cast<sal_uInt8>(((l) >> 24) & 0xff), \ + *((c)++) = static_cast<sal_uInt8>(((l) >> 16) & 0xff), \ + *((c)++) = static_cast<sal_uInt8>(((l) >> 8) & 0xff), \ + *((c)++) = static_cast<sal_uInt8>(((l) ) & 0xff)) + +#define RTL_DIGEST_LTOC(l,c) \ + *((c)++) = static_cast<sal_uInt8>(((l) ) & 0xff); \ + *((c)++) = static_cast<sal_uInt8>(((l) >> 8) & 0xff); \ + *((c)++) = static_cast<sal_uInt8>(((l) >> 16) & 0xff); \ + *((c)++) = static_cast<sal_uInt8>(((l) >> 24) & 0xff); + +typedef rtlDigestError (Digest_init_t) ( + void *ctx, const sal_uInt8 *Data, sal_uInt32 DatLen); + +typedef void (Digest_delete_t) (void *ctx); + +typedef rtlDigestError (Digest_update_t) ( + void *ctx, const void *Data, sal_uInt32 DatLen); + +typedef rtlDigestError (Digest_get_t) ( + void *ctx, sal_uInt8 *Buffer, sal_uInt32 BufLen); + +namespace { + +struct Digest_Impl +{ + rtlDigestAlgorithm m_algorithm; + sal_uInt32 m_length; + + Digest_init_t *m_init; + Digest_delete_t *m_delete; + Digest_update_t *m_update; + Digest_get_t *m_get; +}; + +} + +static void swapLong(sal_uInt32 *pData, sal_uInt32 nDatLen) +{ + sal_uInt32 *X; + int i, n; + + X = pData; + n = nDatLen; + + for (i = 0; i < n; i++) + { + X[i] = OSL_SWAPDWORD(X[i]); + } +} + +rtlDigest SAL_CALL rtl_digest_create(rtlDigestAlgorithm Algorithm) + SAL_THROW_EXTERN_C() +{ + rtlDigest Digest = nullptr; + switch (Algorithm) + { + case rtl_Digest_AlgorithmMD2: + Digest = rtl_digest_createMD2(); + break; + + case rtl_Digest_AlgorithmMD5: + Digest = rtl_digest_createMD5(); + break; + + case rtl_Digest_AlgorithmSHA: + Digest = rtl_digest_createSHA(); + break; + + case rtl_Digest_AlgorithmSHA1: + Digest = rtl_digest_createSHA1(); + break; + + case rtl_Digest_AlgorithmHMAC_MD5: + Digest = rtl_digest_createHMAC_MD5(); + break; + + case rtl_Digest_AlgorithmHMAC_SHA1: + Digest = rtl_digest_createHMAC_SHA1(); + break; + + default: /* rtl_Digest_AlgorithmInvalid */ + break; + } + return Digest; +} + +rtlDigestAlgorithm SAL_CALL rtl_digest_queryAlgorithm(rtlDigest Digest) + SAL_THROW_EXTERN_C() +{ + Digest_Impl *pImpl = static_cast<Digest_Impl *>(Digest); + if (pImpl) + return pImpl->m_algorithm; + return rtl_Digest_AlgorithmInvalid; +} + +sal_uInt32 SAL_CALL rtl_digest_queryLength(rtlDigest Digest) + SAL_THROW_EXTERN_C() +{ + Digest_Impl *pImpl = static_cast<Digest_Impl *>(Digest); + if (pImpl) + return pImpl->m_length; + return 0; +} + +rtlDigestError SAL_CALL rtl_digest_init( + rtlDigest Digest, const sal_uInt8 *pData, sal_uInt32 nDatLen) + SAL_THROW_EXTERN_C() +{ + Digest_Impl *pImpl = static_cast<Digest_Impl *>(Digest); + if (pImpl) + { + if (pImpl->m_init) + return pImpl->m_init (Digest, pData, nDatLen); + return rtl_Digest_E_None; + } + return rtl_Digest_E_Argument; +} + +rtlDigestError SAL_CALL rtl_digest_update( + rtlDigest Digest, const void *pData, sal_uInt32 nDatLen) + SAL_THROW_EXTERN_C() +{ + Digest_Impl *pImpl = static_cast<Digest_Impl *>(Digest); + if (pImpl && pImpl->m_update) + return pImpl->m_update(Digest, pData, nDatLen); + return rtl_Digest_E_Argument; +} + +rtlDigestError SAL_CALL rtl_digest_get( + rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen) + SAL_THROW_EXTERN_C() +{ + Digest_Impl *pImpl = static_cast<Digest_Impl *>(Digest); + if (pImpl && pImpl->m_get) + return pImpl->m_get(Digest, pBuffer, nBufLen); + return rtl_Digest_E_Argument; +} + +void SAL_CALL rtl_digest_destroy(rtlDigest Digest) SAL_THROW_EXTERN_C() +{ + Digest_Impl *pImpl = static_cast<Digest_Impl *>(Digest); + if (pImpl && pImpl->m_delete) + pImpl->m_delete(Digest); +} + +constexpr auto DIGEST_CBLOCK_MD2 = 16; +constexpr auto DIGEST_LBLOCK_MD2 = 16; + +namespace { + +struct DigestContextMD2 +{ + sal_uInt32 m_nDatLen; + sal_uInt8 m_pData[DIGEST_CBLOCK_MD2]; + sal_uInt32 m_state[DIGEST_LBLOCK_MD2]; + sal_uInt32 m_chksum[DIGEST_LBLOCK_MD2]; +}; + +struct DigestMD2_Impl +{ + Digest_Impl m_digest; + DigestContextMD2 m_context; +}; + +} + +static void initMD2 (DigestContextMD2 *ctx); +static void updateMD2 (DigestContextMD2 *ctx); +static void endMD2 (DigestContextMD2 *ctx); + +const sal_uInt32 S[256] = +{ + 0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, + 0x3D, 0x36, 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13, + 0x62, 0xA7, 0x05, 0xF3, 0xC0, 0xC7, 0x73, 0x8C, + 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, 0x82, 0xCA, + 0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16, + 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12, + 0xBE, 0x4E, 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, + 0xA0, 0xFB, 0xF5, 0x8E, 0xBB, 0x2F, 0xEE, 0x7A, + 0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, 0x07, 0x3F, + 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21, + 0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, + 0x35, 0x3E, 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03, + 0xFF, 0x19, 0x30, 0xB3, 0x48, 0xA5, 0xB5, 0xD1, + 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, 0xAA, 0xC6, + 0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6, + 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1, + 0x45, 0x9D, 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, + 0x86, 0x5B, 0xCF, 0x65, 0xE6, 0x2D, 0xA8, 0x02, + 0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, 0xB9, 0xF6, + 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F, + 0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, + 0xC3, 0x5C, 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26, + 0x2C, 0x53, 0x0D, 0x6E, 0x85, 0x28, 0x84, 0x09, + 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, 0x4D, 0x52, + 0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA, + 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A, + 0x78, 0x88, 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, + 0xE9, 0xCB, 0xD5, 0xFE, 0x3B, 0x00, 0x1D, 0x39, + 0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, 0xD0, 0xE4, + 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A, + 0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, + 0xDB, 0x99, 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14, +}; + +const Digest_Impl MD2 = +{ + rtl_Digest_AlgorithmMD2, + RTL_DIGEST_LENGTH_MD2, + nullptr, + rtl_digest_destroyMD2, + rtl_digest_updateMD2, + rtl_digest_getMD2 +}; + +static void initMD2(DigestContextMD2 *ctx) +{ + memset(ctx, 0, sizeof(DigestContextMD2)); +} + +static void updateMD2(DigestContextMD2 *ctx) +{ + sal_uInt8 *X; + sal_uInt32 *sp1, *sp2; + sal_uInt32 i, k, t; + + sal_uInt32 state[48]; + + X = ctx->m_pData; + sp1 = ctx->m_state; + sp2 = ctx->m_chksum; + + k = sp2[DIGEST_LBLOCK_MD2 - 1]; + for (i = 0; i < 16; i++) + { + state[i + 0] = sp1[i]; + state[i + 16] = t = X[i]; + state[i + 32] = t ^ sp1[i]; + k = sp2[i] ^= S[t^k]; + } + + t = 0; + for (i = 0; i < 18; i++) + { + for (k = 0; k < 48; k += 8) + { + t = state[k + 0] ^= S[t]; + t = state[k + 1] ^= S[t]; + t = state[k + 2] ^= S[t]; + t = state[k + 3] ^= S[t]; + t = state[k + 4] ^= S[t]; + t = state[k + 5] ^= S[t]; + t = state[k + 6] ^= S[t]; + t = state[k + 7] ^= S[t]; + } + t = ((t + i) & 0xff); + } + + memcpy(sp1, state, 16 * sizeof(sal_uInt32)); + rtl_secureZeroMemory(state, 48 * sizeof(sal_uInt32)); +} + +static void endMD2(DigestContextMD2 *ctx) +{ + sal_uInt8 *X; + sal_uInt32 *C; + sal_uInt32 i, n; + + X = ctx->m_pData; + C = ctx->m_chksum; + n = DIGEST_CBLOCK_MD2 - ctx->m_nDatLen; + + for (i = ctx->m_nDatLen; i < DIGEST_CBLOCK_MD2; i++) + X[i] = static_cast<sal_uInt8>(n & 0xff); + + updateMD2(ctx); + + for (i = 0; i < DIGEST_CBLOCK_MD2; i++) + X[i] = static_cast<sal_uInt8>(C[i] & 0xff); + updateMD2(ctx); +} + +rtlDigestError SAL_CALL rtl_digest_MD2( + const void *pData, sal_uInt32 nDatLen, + sal_uInt8 *pBuffer, sal_uInt32 nBufLen) SAL_THROW_EXTERN_C() +{ + DigestMD2_Impl digest; + rtlDigestError result; + + digest.m_digest = MD2; + initMD2(&(digest.m_context)); + + result = rtl_digest_updateMD2(&digest, pData, nDatLen); + if (result == rtl_Digest_E_None) + result = rtl_digest_getMD2(&digest, pBuffer, nBufLen); + + rtl_secureZeroMemory(&digest, sizeof(digest)); + return result; +} + +rtlDigest SAL_CALL rtl_digest_createMD2() SAL_THROW_EXTERN_C() +{ + DigestMD2_Impl *pImpl = RTL_DIGEST_CREATE(DigestMD2_Impl); + if (pImpl) + { + pImpl->m_digest = MD2; + initMD2(&(pImpl->m_context)); + } + return static_cast<rtlDigest>(pImpl); +} + +rtlDigestError SAL_CALL rtl_digest_updateMD2( + rtlDigest Digest, const void *pData, sal_uInt32 nDatLen) + SAL_THROW_EXTERN_C() +{ + DigestMD2_Impl *pImpl = static_cast<DigestMD2_Impl *>(Digest); + const sal_uInt8 *d = static_cast<const sal_uInt8 *>(pData); + + DigestContextMD2 *ctx; + + if (!pImpl || !pData) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmMD2) + return rtl_Digest_E_Algorithm; + + if (nDatLen == 0) + return rtl_Digest_E_None; + + ctx = &(pImpl->m_context); + + if (ctx->m_nDatLen) + { + sal_uInt8 *p = ctx->m_pData + ctx->m_nDatLen; + sal_uInt32 n = DIGEST_CBLOCK_MD2 - ctx->m_nDatLen; + + if (nDatLen < n) + { + memcpy(p, d, nDatLen); + ctx->m_nDatLen += nDatLen; + + return rtl_Digest_E_None; + } + + memcpy(p, d, n); + d += n; + nDatLen -= n; + + updateMD2(ctx); + ctx->m_nDatLen = 0; + } + + while (nDatLen >= DIGEST_CBLOCK_MD2) + { + memcpy(ctx->m_pData, d, DIGEST_CBLOCK_MD2); + d += DIGEST_CBLOCK_MD2; + nDatLen -= DIGEST_CBLOCK_MD2; + + updateMD2(ctx); + } + + memcpy(ctx->m_pData, d, nDatLen); + ctx->m_nDatLen = nDatLen; + + return rtl_Digest_E_None; +} + +rtlDigestError SAL_CALL rtl_digest_getMD2( + rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen) + SAL_THROW_EXTERN_C() +{ + DigestMD2_Impl *pImpl = static_cast<DigestMD2_Impl *>(Digest); + sal_uInt32 i; + + DigestContextMD2 *ctx; + + if (!pImpl || !pBuffer) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmMD2) + return rtl_Digest_E_Algorithm; + + if (pImpl->m_digest.m_length > nBufLen) + return rtl_Digest_E_BufferSize; + + ctx = &(pImpl->m_context); + + endMD2(ctx); + for (i = 0; i < DIGEST_CBLOCK_MD2; i++) + { + pBuffer[i] = static_cast<sal_uInt8>(ctx->m_state[i] & 0xff); + } + + initMD2(ctx); + + return rtl_Digest_E_None; +} + +void SAL_CALL rtl_digest_destroyMD2(rtlDigest Digest) SAL_THROW_EXTERN_C() +{ + DigestMD2_Impl *pImpl = static_cast<DigestMD2_Impl *>(Digest); + if (pImpl) + { + if (pImpl->m_digest.m_algorithm == rtl_Digest_AlgorithmMD2) + rtl_freeZeroMemory(pImpl, sizeof(DigestMD2_Impl)); + else + free(pImpl); + } +} + +#define DIGEST_CBLOCK_MD5 64 +#define DIGEST_LBLOCK_MD5 16 + +namespace { + +struct DigestContextMD5 +{ + sal_uInt32 m_nDatLen; + sal_uInt32 m_pData[DIGEST_LBLOCK_MD5]; + sal_uInt32 m_nA, m_nB, m_nC, m_nD; + sal_uInt32 m_nL, m_nH; +}; + +struct DigestMD5_Impl +{ + Digest_Impl m_digest; + DigestContextMD5 m_context; +}; + +} + +static void initMD5 (DigestContextMD5 *ctx); +static void updateMD5 (DigestContextMD5 *ctx); +static void endMD5 (DigestContextMD5 *ctx); + +#define F(x,y,z) ((((y) ^ (z)) & (x)) ^ (z)) +#define G(x,y,z) ((((x) ^ (y)) & (z)) ^ (y)) +#define H(x,y,z) ((x) ^ (y) ^ (z)) +#define I(x,y,z) (((x) | (~(z))) ^ (y)) + +#define R0(a,b,c,d,k,s,t) { \ + a += ((k) + (t) + F((b), (c), (d))); \ + a = RTL_DIGEST_ROTL(a, s); \ + a += b; } + +#define R1(a,b,c,d,k,s,t) { \ + a += ((k) + (t) + G((b), (c), (d))); \ + a = RTL_DIGEST_ROTL(a, s); \ + a += b; } + +#define R2(a,b,c,d,k,s,t) { \ + a += ((k) + (t) + H((b), (c), (d))); \ + a = RTL_DIGEST_ROTL(a, s); \ + a += b; } + +#define R3(a,b,c,d,k,s,t) { \ + a += ((k) + (t) + I((b), (c), (d))); \ + a = RTL_DIGEST_ROTL(a, s); \ + a += b; } + +const Digest_Impl MD5 = +{ + rtl_Digest_AlgorithmMD5, + RTL_DIGEST_LENGTH_MD5, + nullptr, + rtl_digest_destroyMD5, + rtl_digest_updateMD5, + rtl_digest_getMD5 +}; + +static void initMD5(DigestContextMD5 *ctx) +{ + memset(ctx, 0, sizeof(DigestContextMD5)); + + ctx->m_nA = sal_uInt32(0x67452301L); + ctx->m_nB = sal_uInt32(0xefcdab89L); + ctx->m_nC = sal_uInt32(0x98badcfeL); + ctx->m_nD = sal_uInt32(0x10325476L); +} + +static void updateMD5(DigestContextMD5 *ctx) +{ + sal_uInt32 A, B, C, D; + sal_uInt32 *X; + + A = ctx->m_nA; + B = ctx->m_nB; + C = ctx->m_nC; + D = ctx->m_nD; + X = ctx->m_pData; + + R0 (A, B, C, D, X[ 0], 7, 0xd76aa478L); + R0 (D, A, B, C, X[ 1], 12, 0xe8c7b756L); + R0 (C, D, A, B, X[ 2], 17, 0x242070dbL); + R0 (B, C, D, A, X[ 3], 22, 0xc1bdceeeL); + R0 (A, B, C, D, X[ 4], 7, 0xf57c0fafL); + R0 (D, A, B, C, X[ 5], 12, 0x4787c62aL); + R0 (C, D, A, B, X[ 6], 17, 0xa8304613L); + R0 (B, C, D, A, X[ 7], 22, 0xfd469501L); + R0 (A, B, C, D, X[ 8], 7, 0x698098d8L); + R0 (D, A, B, C, X[ 9], 12, 0x8b44f7afL); + R0 (C, D, A, B, X[10], 17, 0xffff5bb1L); + R0 (B, C, D, A, X[11], 22, 0x895cd7beL); + R0 (A, B, C, D, X[12], 7, 0x6b901122L); + R0 (D, A, B, C, X[13], 12, 0xfd987193L); + R0 (C, D, A, B, X[14], 17, 0xa679438eL); + R0 (B, C, D, A, X[15], 22, 0x49b40821L); + + R1 (A, B, C, D, X[ 1], 5, 0xf61e2562L); + R1 (D, A, B, C, X[ 6], 9, 0xc040b340L); + R1 (C, D, A, B, X[11], 14, 0x265e5a51L); + R1 (B, C, D, A, X[ 0], 20, 0xe9b6c7aaL); + R1 (A, B, C, D, X[ 5], 5, 0xd62f105dL); + R1 (D, A, B, C, X[10], 9, 0x02441453L); + R1 (C, D, A, B, X[15], 14, 0xd8a1e681L); + R1 (B, C, D, A, X[ 4], 20, 0xe7d3fbc8L); + R1 (A, B, C, D, X[ 9], 5, 0x21e1cde6L); + R1 (D, A, B, C, X[14], 9, 0xc33707d6L); + R1 (C, D, A, B, X[ 3], 14, 0xf4d50d87L); + R1 (B, C, D, A, X[ 8], 20, 0x455a14edL); + R1 (A, B, C, D, X[13], 5, 0xa9e3e905L); + R1 (D, A, B, C, X[ 2], 9, 0xfcefa3f8L); + R1 (C, D, A, B, X[ 7], 14, 0x676f02d9L); + R1 (B, C, D, A, X[12], 20, 0x8d2a4c8aL); + + R2 (A, B, C, D, X[ 5], 4, 0xfffa3942L); + R2 (D, A, B, C, X[ 8], 11, 0x8771f681L); + R2 (C, D, A, B, X[11], 16, 0x6d9d6122L); + R2 (B, C, D, A, X[14], 23, 0xfde5380cL); + R2 (A, B, C, D, X[ 1], 4, 0xa4beea44L); + R2 (D, A, B, C, X[ 4], 11, 0x4bdecfa9L); + R2 (C, D, A, B, X[ 7], 16, 0xf6bb4b60L); + R2 (B, C, D, A, X[10], 23, 0xbebfbc70L); + R2 (A, B, C, D, X[13], 4, 0x289b7ec6L); + R2 (D, A, B, C, X[ 0], 11, 0xeaa127faL); + R2 (C, D, A, B, X[ 3], 16, 0xd4ef3085L); + R2 (B, C, D, A, X[ 6], 23, 0x04881d05L); + R2 (A, B, C, D, X[ 9], 4, 0xd9d4d039L); + R2 (D, A, B, C, X[12], 11, 0xe6db99e5L); + R2 (C, D, A, B, X[15], 16, 0x1fa27cf8L); + R2 (B, C, D, A, X[ 2], 23, 0xc4ac5665L); + + R3 (A, B, C, D, X[ 0], 6, 0xf4292244L); + R3 (D, A, B, C, X[ 7], 10, 0x432aff97L); + R3 (C, D, A, B, X[14], 15, 0xab9423a7L); + R3 (B, C, D, A, X[ 5], 21, 0xfc93a039L); + R3 (A, B, C, D, X[12], 6, 0x655b59c3L); + R3 (D, A, B, C, X[ 3], 10, 0x8f0ccc92L); + R3 (C, D, A, B, X[10], 15, 0xffeff47dL); + R3 (B, C, D, A, X[ 1], 21, 0x85845dd1L); + R3 (A, B, C, D, X[ 8], 6, 0x6fa87e4fL); + R3 (D, A, B, C, X[15], 10, 0xfe2ce6e0L); + R3 (C, D, A, B, X[ 6], 15, 0xa3014314L); + R3 (B, C, D, A, X[13], 21, 0x4e0811a1L); + R3 (A, B, C, D, X[ 4], 6, 0xf7537e82L); + R3 (D, A, B, C, X[11], 10, 0xbd3af235L); + R3 (C, D, A, B, X[ 2], 15, 0x2ad7d2bbL); + R3 (B, C, D, A, X[ 9], 21, 0xeb86d391L); + + ctx->m_nA += A; + ctx->m_nB += B; + ctx->m_nC += C; + ctx->m_nD += D; +} + +static void endMD5(DigestContextMD5 *ctx) +{ + static const sal_uInt8 end[4] = + { + 0x80, 0x00, 0x00, 0x00 + }; + const sal_uInt8 *p = end; + + sal_uInt32 *X; + int i; + + X = ctx->m_pData; + i = (ctx->m_nDatLen >> 2); + +#ifdef OSL_BIGENDIAN + swapLong(X, i + 1); +#endif /* OSL_BIGENDIAN */ + + switch (ctx->m_nDatLen & 0x03) + { + case 1: X[i] &= 0x000000ff; break; + case 2: X[i] &= 0x0000ffff; break; + case 3: X[i] &= 0x00ffffff; break; + } + + switch (ctx->m_nDatLen & 0x03) + { + case 0: X[i] = static_cast<sal_uInt32>(*(p++)) << 0; + [[fallthrough]]; + case 1: X[i] |= static_cast<sal_uInt32>(*(p++)) << 8; + [[fallthrough]]; + case 2: X[i] |= static_cast<sal_uInt32>(*(p++)) << 16; + [[fallthrough]]; + case 3: X[i] |= static_cast<sal_uInt32>(*p) << 24; + } + + i += 1; + + if (i > (DIGEST_LBLOCK_MD5 - 2)) + { + for (; i < DIGEST_LBLOCK_MD5; i++) + { + X[i] = 0; + } + + updateMD5(ctx); + i = 0; + } + + for (; i < (DIGEST_LBLOCK_MD5 - 2); i++) + X[i] = 0; + + X[DIGEST_LBLOCK_MD5 - 2] = ctx->m_nL; + X[DIGEST_LBLOCK_MD5 - 1] = ctx->m_nH; + + updateMD5(ctx); +} + +rtlDigestError SAL_CALL rtl_digest_MD5( + const void *pData, sal_uInt32 nDatLen, + sal_uInt8 *pBuffer, sal_uInt32 nBufLen) SAL_THROW_EXTERN_C() +{ + DigestMD5_Impl digest; + rtlDigestError result; + + digest.m_digest = MD5; + initMD5(&(digest.m_context)); + + result = rtl_digest_update(&digest, pData, nDatLen); + if (result == rtl_Digest_E_None) + result = rtl_digest_getMD5(&digest, pBuffer, nBufLen); + + rtl_secureZeroMemory(&digest, sizeof(digest)); + return result; +} + +rtlDigest SAL_CALL rtl_digest_createMD5() SAL_THROW_EXTERN_C() +{ + DigestMD5_Impl *pImpl = RTL_DIGEST_CREATE(DigestMD5_Impl); + if (pImpl) + { + pImpl->m_digest = MD5; + initMD5(&(pImpl->m_context)); + } + return static_cast<rtlDigest>(pImpl); +} + +rtlDigestError SAL_CALL rtl_digest_updateMD5( + rtlDigest Digest, const void *pData, sal_uInt32 nDatLen) + SAL_THROW_EXTERN_C() +{ + DigestMD5_Impl *pImpl = static_cast<DigestMD5_Impl *>(Digest); + const sal_uInt8 *d = static_cast<const sal_uInt8 *>(pData); + + DigestContextMD5 *ctx; + sal_uInt32 len; + + if (!pImpl || !pData) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmMD5) + return rtl_Digest_E_Algorithm; + + if (nDatLen == 0) + return rtl_Digest_E_None; + + ctx = &(pImpl->m_context); + + len = ctx->m_nL + (nDatLen << 3); + if (len < ctx->m_nL) + ctx->m_nH += 1; + + ctx->m_nH += (nDatLen >> 29); + ctx->m_nL = len; + + if (ctx->m_nDatLen) + { + sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(ctx->m_pData) + ctx->m_nDatLen; + sal_uInt32 n = DIGEST_CBLOCK_MD5 - ctx->m_nDatLen; + + if (nDatLen < n) + { + memcpy(p, d, nDatLen); + ctx->m_nDatLen += nDatLen; + + return rtl_Digest_E_None; + } + + memcpy(p, d, n); + d += n; + nDatLen -= n; + +#ifdef OSL_BIGENDIAN + swapLong(ctx->m_pData, DIGEST_LBLOCK_MD5); +#endif /* OSL_BIGENDIAN */ + + updateMD5(ctx); + ctx->m_nDatLen = 0; + } + + while (nDatLen >= DIGEST_CBLOCK_MD5) + { + memcpy(ctx->m_pData, d, DIGEST_CBLOCK_MD5); + d += DIGEST_CBLOCK_MD5; + nDatLen -= DIGEST_CBLOCK_MD5; + +#ifdef OSL_BIGENDIAN + swapLong(ctx->m_pData, DIGEST_LBLOCK_MD5); +#endif /* OSL_BIGENDIAN */ + + updateMD5(ctx); + } + + memcpy(ctx->m_pData, d, nDatLen); + ctx->m_nDatLen = nDatLen; + + return rtl_Digest_E_None; +} + +rtlDigestError SAL_CALL rtl_digest_getMD5( + rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen) + SAL_THROW_EXTERN_C() +{ + DigestMD5_Impl *pImpl = static_cast<DigestMD5_Impl *>(Digest); + sal_uInt8 *p = pBuffer; + + DigestContextMD5 *ctx; + + if (!pImpl || !pBuffer) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmMD5) + return rtl_Digest_E_Algorithm; + + if (pImpl->m_digest.m_length > nBufLen) + return rtl_Digest_E_BufferSize; + + ctx = &(pImpl->m_context); + + endMD5(ctx); + RTL_DIGEST_LTOC(ctx->m_nA, p); + RTL_DIGEST_LTOC(ctx->m_nB, p); + RTL_DIGEST_LTOC(ctx->m_nC, p); + RTL_DIGEST_LTOC(ctx->m_nD, p); + initMD5(ctx); + + return rtl_Digest_E_None; +} + +rtlDigestError SAL_CALL rtl_digest_rawMD5( + rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen) + SAL_THROW_EXTERN_C() +{ + DigestMD5_Impl *pImpl = static_cast<DigestMD5_Impl *>(Digest); + sal_uInt8 *p = pBuffer; + + DigestContextMD5 *ctx; + + if (!pImpl || !pBuffer) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmMD5) + return rtl_Digest_E_Algorithm; + + if (pImpl->m_digest.m_length > nBufLen) + return rtl_Digest_E_BufferSize; + + ctx = &(pImpl->m_context); + + /* not finalized */ + RTL_DIGEST_LTOC(ctx->m_nA, p); + RTL_DIGEST_LTOC(ctx->m_nB, p); + RTL_DIGEST_LTOC(ctx->m_nC, p); + RTL_DIGEST_LTOC(ctx->m_nD, p); + initMD5(ctx); + + return rtl_Digest_E_None; +} + +void SAL_CALL rtl_digest_destroyMD5(rtlDigest Digest) SAL_THROW_EXTERN_C() +{ + DigestMD5_Impl *pImpl = static_cast<DigestMD5_Impl *>(Digest); + if (pImpl) + { + if (pImpl->m_digest.m_algorithm == rtl_Digest_AlgorithmMD5) + rtl_freeZeroMemory(pImpl, sizeof(DigestMD5_Impl)); + else + free(pImpl); + } +} + +#define DIGEST_CBLOCK_SHA 64 +#define DIGEST_LBLOCK_SHA 16 + +typedef sal_uInt32 DigestSHA_update_t(sal_uInt32 x); + +static sal_uInt32 updateSHA_0(sal_uInt32 x); +static sal_uInt32 updateSHA_1(sal_uInt32 x); + +namespace { + +struct DigestContextSHA +{ + DigestSHA_update_t *m_update; + sal_uInt32 m_nDatLen; + sal_uInt32 m_pData[DIGEST_LBLOCK_SHA]; + sal_uInt32 m_nA, m_nB, m_nC, m_nD, m_nE; + sal_uInt32 m_nL, m_nH; +}; + +struct DigestSHA_Impl +{ + Digest_Impl m_digest; + DigestContextSHA m_context; +}; + +} + +static void initSHA( + DigestContextSHA *ctx, DigestSHA_update_t *fct); + +static void updateSHA(DigestContextSHA *ctx); +static void endSHA(DigestContextSHA *ctx); + +#define K_00_19 sal_uInt32(0x5a827999L) +#define K_20_39 sal_uInt32(0x6ed9eba1L) +#define K_40_59 sal_uInt32(0x8f1bbcdcL) +#define K_60_79 sal_uInt32(0xca62c1d6L) + +#define F_00_19(b,c,d) ((((c) ^ (d)) & (b)) ^ (d)) +#define F_20_39(b,c,d) ((b) ^ (c) ^ (d)) +#define F_40_59(b,c,d) (((b) & (c)) | ((b) & (d)) | ((c) & (d))) +#define F_60_79(b,c,d) F_20_39(b,c,d) + +#define BODY_X(i) \ + (X[(i)&0x0f] ^ X[((i)+2)&0x0f] ^ X[((i)+8)&0x0f] ^ X[((i)+13)&0x0f]) + +#define BODY_00_15(u,i,a,b,c,d,e,f) \ + (f) = X[i]; \ + (f) += (e) + K_00_19 + RTL_DIGEST_ROTL((a), 5) + F_00_19((b), (c), (d)); \ + (b) = RTL_DIGEST_ROTL((b), 30); + +#define BODY_16_19(u,i,a,b,c,d,e,f) \ + (f) = BODY_X((i)); \ + (f) = X[(i)&0x0f] = (u)((f)); \ + (f) += (e) + K_00_19 + RTL_DIGEST_ROTL((a), 5) + F_00_19((b), (c), (d)); \ + (b) = RTL_DIGEST_ROTL((b), 30); + +#define BODY_20_39(u,i,a,b,c,d,e,f) \ + (f) = BODY_X((i)); \ + (f) = X[(i)&0x0f] = (u)((f)); \ + (f) += (e) + K_20_39 + RTL_DIGEST_ROTL((a), 5) + F_20_39((b), (c), (d)); \ + (b) = RTL_DIGEST_ROTL((b), 30); + +#define BODY_40_59(u,i,a,b,c,d,e,f) \ + (f) = BODY_X((i)); \ + (f) = X[(i)&0x0f] = (u)((f)); \ + (f) += (e) + K_40_59 + RTL_DIGEST_ROTL((a), 5) + F_40_59((b), (c), (d)); \ + (b) = RTL_DIGEST_ROTL((b), 30); + +#define BODY_60_79(u,i,a,b,c,d,e,f) \ + (f) = BODY_X((i)); \ + (f) = X[(i)&0x0f] = (u)((f)); \ + (f) += (e) + K_60_79 + RTL_DIGEST_ROTL((a), 5) + F_60_79((b), (c), (d)); \ + (b) = RTL_DIGEST_ROTL((b), 30); + +static void initSHA( + DigestContextSHA *ctx, DigestSHA_update_t *fct) +{ + memset(ctx, 0, sizeof(DigestContextSHA)); + ctx->m_update = fct; + + ctx->m_nA = sal_uInt32(0x67452301L); + ctx->m_nB = sal_uInt32(0xefcdab89L); + ctx->m_nC = sal_uInt32(0x98badcfeL); + ctx->m_nD = sal_uInt32(0x10325476L); + ctx->m_nE = sal_uInt32(0xc3d2e1f0L); +} + +static void updateSHA(DigestContextSHA *ctx) +{ + sal_uInt32 A, B, C, D, E, T; + sal_uInt32 *X; + + DigestSHA_update_t *U; + U = ctx->m_update; + + A = ctx->m_nA; + B = ctx->m_nB; + C = ctx->m_nC; + D = ctx->m_nD; + E = ctx->m_nE; + X = ctx->m_pData; + + BODY_00_15 (U, 0, A, B, C, D, E, T); + BODY_00_15 (U, 1, T, A, B, C, D, E); + BODY_00_15 (U, 2, E, T, A, B, C, D); + BODY_00_15 (U, 3, D, E, T, A, B, C); + BODY_00_15 (U, 4, C, D, E, T, A, B); + BODY_00_15 (U, 5, B, C, D, E, T, A); + BODY_00_15 (U, 6, A, B, C, D, E, T); + BODY_00_15 (U, 7, T, A, B, C, D, E); + BODY_00_15 (U, 8, E, T, A, B, C, D); + BODY_00_15 (U, 9, D, E, T, A, B, C); + BODY_00_15 (U, 10, C, D, E, T, A, B); + BODY_00_15 (U, 11, B, C, D, E, T, A); + BODY_00_15 (U, 12, A, B, C, D, E, T); + BODY_00_15 (U, 13, T, A, B, C, D, E); + BODY_00_15 (U, 14, E, T, A, B, C, D); + BODY_00_15 (U, 15, D, E, T, A, B, C); + BODY_16_19 (U, 16, C, D, E, T, A, B); + BODY_16_19 (U, 17, B, C, D, E, T, A); + BODY_16_19 (U, 18, A, B, C, D, E, T); + BODY_16_19 (U, 19, T, A, B, C, D, E); + + BODY_20_39 (U, 20, E, T, A, B, C, D); + BODY_20_39 (U, 21, D, E, T, A, B, C); + BODY_20_39 (U, 22, C, D, E, T, A, B); + BODY_20_39 (U, 23, B, C, D, E, T, A); + BODY_20_39 (U, 24, A, B, C, D, E, T); + BODY_20_39 (U, 25, T, A, B, C, D, E); + BODY_20_39 (U, 26, E, T, A, B, C, D); + BODY_20_39 (U, 27, D, E, T, A, B, C); + BODY_20_39 (U, 28, C, D, E, T, A, B); + BODY_20_39 (U, 29, B, C, D, E, T, A); + BODY_20_39 (U, 30, A, B, C, D, E, T); + BODY_20_39 (U, 31, T, A, B, C, D, E); + BODY_20_39 (U, 32, E, T, A, B, C, D); + BODY_20_39 (U, 33, D, E, T, A, B, C); + BODY_20_39 (U, 34, C, D, E, T, A, B); + BODY_20_39 (U, 35, B, C, D, E, T, A); + BODY_20_39 (U, 36, A, B, C, D, E, T); + BODY_20_39 (U, 37, T, A, B, C, D, E); + BODY_20_39 (U, 38, E, T, A, B, C, D); + BODY_20_39 (U, 39, D, E, T, A, B, C); + + BODY_40_59 (U, 40, C, D, E, T, A, B); + BODY_40_59 (U, 41, B, C, D, E, T, A); + BODY_40_59 (U, 42, A, B, C, D, E, T); + BODY_40_59 (U, 43, T, A, B, C, D, E); + BODY_40_59 (U, 44, E, T, A, B, C, D); + BODY_40_59 (U, 45, D, E, T, A, B, C); + BODY_40_59 (U, 46, C, D, E, T, A, B); + BODY_40_59 (U, 47, B, C, D, E, T, A); + BODY_40_59 (U, 48, A, B, C, D, E, T); + BODY_40_59 (U, 49, T, A, B, C, D, E); + BODY_40_59 (U, 50, E, T, A, B, C, D); + BODY_40_59 (U, 51, D, E, T, A, B, C); + BODY_40_59 (U, 52, C, D, E, T, A, B); + BODY_40_59 (U, 53, B, C, D, E, T, A); + BODY_40_59 (U, 54, A, B, C, D, E, T); + BODY_40_59 (U, 55, T, A, B, C, D, E); + BODY_40_59 (U, 56, E, T, A, B, C, D); + BODY_40_59 (U, 57, D, E, T, A, B, C); + BODY_40_59 (U, 58, C, D, E, T, A, B); + BODY_40_59 (U, 59, B, C, D, E, T, A); + + BODY_60_79 (U, 60, A, B, C, D, E, T); + BODY_60_79 (U, 61, T, A, B, C, D, E); + BODY_60_79 (U, 62, E, T, A, B, C, D); + BODY_60_79 (U, 63, D, E, T, A, B, C); + BODY_60_79 (U, 64, C, D, E, T, A, B); + BODY_60_79 (U, 65, B, C, D, E, T, A); + BODY_60_79 (U, 66, A, B, C, D, E, T); + BODY_60_79 (U, 67, T, A, B, C, D, E); + BODY_60_79 (U, 68, E, T, A, B, C, D); + BODY_60_79 (U, 69, D, E, T, A, B, C); + BODY_60_79 (U, 70, C, D, E, T, A, B); + BODY_60_79 (U, 71, B, C, D, E, T, A); + BODY_60_79 (U, 72, A, B, C, D, E, T); + BODY_60_79 (U, 73, T, A, B, C, D, E); + BODY_60_79 (U, 74, E, T, A, B, C, D); + BODY_60_79 (U, 75, D, E, T, A, B, C); + BODY_60_79 (U, 76, C, D, E, T, A, B); + BODY_60_79 (U, 77, B, C, D, E, T, A); + BODY_60_79 (U, 78, A, B, C, D, E, T); + BODY_60_79 (U, 79, T, A, B, C, D, E); + + ctx->m_nA += E; + ctx->m_nB += T; + ctx->m_nC += A; + ctx->m_nD += B; + ctx->m_nE += C; +} + +static void endSHA(DigestContextSHA *ctx) +{ + static const sal_uInt8 end[4] = + { + 0x80, 0x00, 0x00, 0x00 + }; + const sal_uInt8 *p = end; + + sal_uInt32 *X; + int i; + + X = ctx->m_pData; + i = (ctx->m_nDatLen >> 2); + +#ifdef OSL_BIGENDIAN + swapLong(X, i + 1); +#endif /* OSL_BIGENDIAN */ + + switch (ctx->m_nDatLen & 0x03) + { + case 1: X[i] &= 0x000000ff; break; + case 2: X[i] &= 0x0000ffff; break; + case 3: X[i] &= 0x00ffffff; break; + } + + switch (ctx->m_nDatLen & 0x03) + { + case 0: X[i] = static_cast<sal_uInt32>(*(p++)) << 0; + [[fallthrough]]; + case 1: X[i] |= static_cast<sal_uInt32>(*(p++)) << 8; + [[fallthrough]]; + case 2: X[i] |= static_cast<sal_uInt32>(*(p++)) << 16; + [[fallthrough]]; + case 3: X[i] |= static_cast<sal_uInt32>(*(p++)) << 24; + } + + swapLong(X, i + 1); + + i += 1; + + // tdf#114939 NB: this is WRONG and should be ">" not ">=" but is not + // fixed as this buggy SHA1 implementation is needed for compatibility + if (i >= (DIGEST_LBLOCK_SHA - 2)) + { + for (; i < DIGEST_LBLOCK_SHA; i++) + { + X[i] = 0; + } + + updateSHA(ctx); + i = 0; + } + + for (; i < (DIGEST_LBLOCK_SHA - 2); i++) + { + X[i] = 0; + } + + X[DIGEST_LBLOCK_SHA - 2] = ctx->m_nH; + X[DIGEST_LBLOCK_SHA - 1] = ctx->m_nL; + + updateSHA(ctx); +} + +const Digest_Impl SHA_0 = +{ + rtl_Digest_AlgorithmSHA, + RTL_DIGEST_LENGTH_SHA, + nullptr, + rtl_digest_destroySHA, + rtl_digest_updateSHA, + rtl_digest_getSHA +}; + +static sal_uInt32 updateSHA_0(sal_uInt32 x) +{ + return x; +} + +rtlDigestError SAL_CALL rtl_digest_SHA( + const void *pData, sal_uInt32 nDatLen, + sal_uInt8 *pBuffer, sal_uInt32 nBufLen) SAL_THROW_EXTERN_C() +{ + DigestSHA_Impl digest; + rtlDigestError result; + + digest.m_digest = SHA_0; + initSHA(&(digest.m_context), updateSHA_0); + + result = rtl_digest_updateSHA(&digest, pData, nDatLen); + if (result == rtl_Digest_E_None) + result = rtl_digest_getSHA(&digest, pBuffer, nBufLen); + + rtl_secureZeroMemory(&digest, sizeof(digest)); + return result; +} + +rtlDigest SAL_CALL rtl_digest_createSHA() SAL_THROW_EXTERN_C() +{ + DigestSHA_Impl *pImpl = RTL_DIGEST_CREATE(DigestSHA_Impl); + if (pImpl) + { + pImpl->m_digest = SHA_0; + initSHA(&(pImpl->m_context), updateSHA_0); + } + return static_cast<rtlDigest>(pImpl); +} + +rtlDigestError SAL_CALL rtl_digest_updateSHA( + rtlDigest Digest, const void *pData, sal_uInt32 nDatLen) + SAL_THROW_EXTERN_C() +{ + DigestSHA_Impl *pImpl = static_cast<DigestSHA_Impl *>(Digest); + const sal_uInt8 *d = static_cast<const sal_uInt8 *>(pData); + + DigestContextSHA *ctx; + sal_uInt32 len; + + if (!pImpl || !pData) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmSHA) + return rtl_Digest_E_Algorithm; + + if (nDatLen == 0) + return rtl_Digest_E_None; + + ctx = &(pImpl->m_context); + + len = ctx->m_nL + (nDatLen << 3); + if (len < ctx->m_nL) + ctx->m_nH += 1; + + ctx->m_nH += (nDatLen >> 29); + ctx->m_nL = len; + + if (ctx->m_nDatLen) + { + sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(ctx->m_pData) + ctx->m_nDatLen; + sal_uInt32 n = DIGEST_CBLOCK_SHA - ctx->m_nDatLen; + + if (nDatLen < n) + { + memcpy(p, d, nDatLen); + ctx->m_nDatLen += nDatLen; + + return rtl_Digest_E_None; + } + + memcpy(p, d, n); + d += n; + nDatLen -= n; + +#ifndef OSL_BIGENDIAN + swapLong(ctx->m_pData, DIGEST_LBLOCK_SHA); +#endif /* OSL_BIGENDIAN */ + + updateSHA(ctx); + ctx->m_nDatLen = 0; + } + + while (nDatLen >= DIGEST_CBLOCK_SHA) + { + memcpy(ctx->m_pData, d, DIGEST_CBLOCK_SHA); + d += DIGEST_CBLOCK_SHA; + nDatLen -= DIGEST_CBLOCK_SHA; + +#ifndef OSL_BIGENDIAN + swapLong(ctx->m_pData, DIGEST_LBLOCK_SHA); +#endif /* OSL_BIGENDIAN */ + + updateSHA(ctx); + } + + memcpy(ctx->m_pData, d, nDatLen); + ctx->m_nDatLen = nDatLen; + + return rtl_Digest_E_None; +} + +rtlDigestError SAL_CALL rtl_digest_getSHA( + rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen) + SAL_THROW_EXTERN_C() +{ + DigestSHA_Impl *pImpl = static_cast<DigestSHA_Impl *>(Digest); + sal_uInt8 *p = pBuffer; + + DigestContextSHA *ctx; + + if (!pImpl || !pBuffer) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmSHA) + return rtl_Digest_E_Algorithm; + + if (pImpl->m_digest.m_length > nBufLen) + return rtl_Digest_E_BufferSize; + + ctx = &(pImpl->m_context); + + endSHA(ctx); + RTL_DIGEST_HTONL(ctx->m_nA, p); + RTL_DIGEST_HTONL(ctx->m_nB, p); + RTL_DIGEST_HTONL(ctx->m_nC, p); + RTL_DIGEST_HTONL(ctx->m_nD, p); + RTL_DIGEST_HTONL(ctx->m_nE, p); + initSHA(ctx, updateSHA_0); + + return rtl_Digest_E_None; +} + +void SAL_CALL rtl_digest_destroySHA(rtlDigest Digest) SAL_THROW_EXTERN_C() +{ + DigestSHA_Impl *pImpl = static_cast< DigestSHA_Impl * >(Digest); + if (pImpl) + { + if (pImpl->m_digest.m_algorithm == rtl_Digest_AlgorithmSHA) + rtl_freeZeroMemory(pImpl, sizeof(DigestSHA_Impl)); + else + free(pImpl); + } +} + +const Digest_Impl SHA_1 = +{ + rtl_Digest_AlgorithmSHA1, + RTL_DIGEST_LENGTH_SHA1, + nullptr, + rtl_digest_destroySHA1, + rtl_digest_updateSHA1, + rtl_digest_getSHA1 +}; + +static sal_uInt32 updateSHA_1(sal_uInt32 x) +{ + return RTL_DIGEST_ROTL(x, 1); +} + +rtlDigestError SAL_CALL rtl_digest_SHA1( + const void *pData, sal_uInt32 nDatLen, + sal_uInt8 *pBuffer, sal_uInt32 nBufLen) SAL_THROW_EXTERN_C() +{ + DigestSHA_Impl digest; + rtlDigestError result; + + digest.m_digest = SHA_1; + initSHA(&(digest.m_context), updateSHA_1); + + result = rtl_digest_updateSHA1(&digest, pData, nDatLen); + if (result == rtl_Digest_E_None) + result = rtl_digest_getSHA1(&digest, pBuffer, nBufLen); + + rtl_secureZeroMemory(&digest, sizeof(digest)); + return result; +} + +rtlDigest SAL_CALL rtl_digest_createSHA1() SAL_THROW_EXTERN_C() +{ + DigestSHA_Impl *pImpl = RTL_DIGEST_CREATE(DigestSHA_Impl); + if (pImpl) + { + pImpl->m_digest = SHA_1; + initSHA(&(pImpl->m_context), updateSHA_1); + } + return static_cast<rtlDigest>(pImpl); +} + +rtlDigestError SAL_CALL rtl_digest_updateSHA1( + rtlDigest Digest, const void *pData, sal_uInt32 nDatLen) + SAL_THROW_EXTERN_C() +{ + DigestSHA_Impl *pImpl = static_cast< DigestSHA_Impl * >(Digest); + const sal_uInt8 *d = static_cast< const sal_uInt8 * >(pData); + + DigestContextSHA *ctx; + sal_uInt32 len; + + if (!pImpl || !pData) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmSHA1) + return rtl_Digest_E_Algorithm; + + if (nDatLen == 0) + return rtl_Digest_E_None; + + ctx = &(pImpl->m_context); + + len = ctx->m_nL + (nDatLen << 3); + if (len < ctx->m_nL) + ctx->m_nH += 1; + + ctx->m_nH += (nDatLen >> 29); + ctx->m_nL = len; + + if (ctx->m_nDatLen) + { + sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(ctx->m_pData) + ctx->m_nDatLen; + sal_uInt32 n = DIGEST_CBLOCK_SHA - ctx->m_nDatLen; + + if (nDatLen < n) + { + memcpy(p, d, nDatLen); + ctx->m_nDatLen += nDatLen; + + return rtl_Digest_E_None; + } + + memcpy(p, d, n); + d += n; + nDatLen -= n; + +#ifndef OSL_BIGENDIAN + swapLong(ctx->m_pData, DIGEST_LBLOCK_SHA); +#endif /* OSL_BIGENDIAN */ + + updateSHA(ctx); + ctx->m_nDatLen = 0; + } + + while (nDatLen >= DIGEST_CBLOCK_SHA) + { + memcpy(ctx->m_pData, d, DIGEST_CBLOCK_SHA); + d += DIGEST_CBLOCK_SHA; + nDatLen -= DIGEST_CBLOCK_SHA; + +#ifndef OSL_BIGENDIAN + swapLong(ctx->m_pData, DIGEST_LBLOCK_SHA); +#endif /* OSL_BIGENDIAN */ + + updateSHA(ctx); + } + + memcpy(ctx->m_pData, d, nDatLen); + ctx->m_nDatLen = nDatLen; + + return rtl_Digest_E_None; +} + +rtlDigestError SAL_CALL rtl_digest_getSHA1 ( + rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen) + SAL_THROW_EXTERN_C() +{ + DigestSHA_Impl *pImpl = static_cast<DigestSHA_Impl *>(Digest); + sal_uInt8 *p = pBuffer; + + DigestContextSHA *ctx; + + if (!pImpl || !pBuffer) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmSHA1) + return rtl_Digest_E_Algorithm; + + if (pImpl->m_digest.m_length > nBufLen) + return rtl_Digest_E_BufferSize; + + ctx = &(pImpl->m_context); + + endSHA(ctx); + RTL_DIGEST_HTONL(ctx->m_nA, p); + RTL_DIGEST_HTONL(ctx->m_nB, p); + RTL_DIGEST_HTONL(ctx->m_nC, p); + RTL_DIGEST_HTONL(ctx->m_nD, p); + RTL_DIGEST_HTONL(ctx->m_nE, p); + initSHA(ctx, updateSHA_1); + + return rtl_Digest_E_None; +} + +void SAL_CALL rtl_digest_destroySHA1(rtlDigest Digest) SAL_THROW_EXTERN_C() +{ + DigestSHA_Impl *pImpl = static_cast< DigestSHA_Impl * >(Digest); + if (pImpl) + { + if (pImpl->m_digest.m_algorithm == rtl_Digest_AlgorithmSHA1) + rtl_freeZeroMemory(pImpl, sizeof(DigestSHA_Impl)); + else + free(pImpl); + } +} + +#define DIGEST_CBLOCK_HMAC_MD5 64 + +namespace { + +struct ContextHMAC_MD5 +{ + DigestMD5_Impl m_hash; + sal_uInt8 m_opad[DIGEST_CBLOCK_HMAC_MD5]; +}; + +struct DigestHMAC_MD5_Impl +{ + Digest_Impl m_digest; + ContextHMAC_MD5 m_context; +}; + +} + +static void initHMAC_MD5(ContextHMAC_MD5 * ctx); +static void ipadHMAC_MD5(ContextHMAC_MD5 * ctx); +static void opadHMAC_MD5(ContextHMAC_MD5 * ctx); + +const Digest_Impl HMAC_MD5 = +{ + rtl_Digest_AlgorithmHMAC_MD5, + RTL_DIGEST_LENGTH_MD5, + + rtl_digest_initHMAC_MD5, + rtl_digest_destroyHMAC_MD5, + rtl_digest_updateHMAC_MD5, + rtl_digest_getHMAC_MD5 +}; + +static void initHMAC_MD5(ContextHMAC_MD5 * ctx) +{ + DigestMD5_Impl *pImpl = &(ctx->m_hash); + + pImpl->m_digest = MD5; + initMD5(&(pImpl->m_context)); + + memset(ctx->m_opad, 0, DIGEST_CBLOCK_HMAC_MD5); +} + +static void ipadHMAC_MD5(ContextHMAC_MD5 * ctx) +{ + sal_uInt32 i; + + for (i = 0; i < DIGEST_CBLOCK_HMAC_MD5; i++) + { + ctx->m_opad[i] ^= 0x36; + } + + rtl_digest_updateMD5(&(ctx->m_hash), ctx->m_opad, DIGEST_CBLOCK_HMAC_MD5); + + for (i = 0; i < DIGEST_CBLOCK_HMAC_MD5; i++) + { + ctx->m_opad[i] ^= 0x36; + } +} + +static void opadHMAC_MD5(ContextHMAC_MD5 * ctx) +{ + sal_uInt32 i; + + for (i = 0; i < DIGEST_CBLOCK_HMAC_MD5; i++) + { + ctx->m_opad[i] ^= 0x5c; + } +} + +rtlDigestError SAL_CALL rtl_digest_HMAC_MD5( + const sal_uInt8 *pKeyData, sal_uInt32 nKeyLen, + const void *pData, sal_uInt32 nDatLen, + sal_uInt8 *pBuffer, sal_uInt32 nBufLen) SAL_THROW_EXTERN_C() +{ + DigestHMAC_MD5_Impl digest; + rtlDigestError result; + + digest.m_digest = HMAC_MD5; + + result = rtl_digest_initHMAC_MD5(&digest, pKeyData, nKeyLen); + if (result == rtl_Digest_E_None) + { + result = rtl_digest_updateHMAC_MD5(&digest, pData, nDatLen); + if (result == rtl_Digest_E_None) + result = rtl_digest_getHMAC_MD5(&digest, pBuffer, nBufLen); + } + + rtl_secureZeroMemory(&digest, sizeof(digest)); + return result; +} + +rtlDigest SAL_CALL rtl_digest_createHMAC_MD5() SAL_THROW_EXTERN_C() +{ + DigestHMAC_MD5_Impl *pImpl = RTL_DIGEST_CREATE(DigestHMAC_MD5_Impl); + if (pImpl) + { + pImpl->m_digest = HMAC_MD5; + initHMAC_MD5(&(pImpl->m_context)); + } + return static_cast< rtlDigest >(pImpl); +} + +rtlDigestError SAL_CALL rtl_digest_initHMAC_MD5( + rtlDigest Digest, const sal_uInt8 *pKeyData, sal_uInt32 nKeyLen) + SAL_THROW_EXTERN_C() +{ + DigestHMAC_MD5_Impl *pImpl = static_cast< DigestHMAC_MD5_Impl* >(Digest); + ContextHMAC_MD5 *ctx; + + if (!pImpl || !pKeyData) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmHMAC_MD5) + return rtl_Digest_E_Algorithm; + + ctx = &(pImpl->m_context); + initHMAC_MD5(ctx); + + if (nKeyLen > DIGEST_CBLOCK_HMAC_MD5) + { + /* Initialize 'opad' with hashed 'KeyData' */ + rtl_digest_updateMD5(&(ctx->m_hash), pKeyData, nKeyLen); + rtl_digest_getMD5(&(ctx->m_hash), ctx->m_opad, RTL_DIGEST_LENGTH_MD5); + } + else + { + /* Initialize 'opad' with plain 'KeyData' */ + memcpy(ctx->m_opad, pKeyData, nKeyLen); + } + + ipadHMAC_MD5(ctx); + opadHMAC_MD5(ctx); + + return rtl_Digest_E_None; +} + +rtlDigestError SAL_CALL rtl_digest_updateHMAC_MD5( + rtlDigest Digest, const void *pData, sal_uInt32 nDatLen) + SAL_THROW_EXTERN_C() +{ + DigestHMAC_MD5_Impl *pImpl = static_cast< DigestHMAC_MD5_Impl* >(Digest); + ContextHMAC_MD5 *ctx; + + if (!pImpl || !pData) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmHMAC_MD5) + return rtl_Digest_E_Algorithm; + + ctx = &(pImpl->m_context); + rtl_digest_updateMD5(&(ctx->m_hash), pData, nDatLen); + + return rtl_Digest_E_None; +} + +rtlDigestError SAL_CALL rtl_digest_getHMAC_MD5( + rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen) + SAL_THROW_EXTERN_C() +{ + DigestHMAC_MD5_Impl *pImpl = static_cast<DigestHMAC_MD5_Impl*>(Digest); + ContextHMAC_MD5 *ctx; + + if (!pImpl || !pBuffer) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmHMAC_MD5) + return rtl_Digest_E_Algorithm; + + if (pImpl->m_digest.m_length > nBufLen) + return rtl_Digest_E_BufferSize; + + nBufLen = pImpl->m_digest.m_length; + + ctx = &(pImpl->m_context); + rtl_digest_getMD5(&(ctx->m_hash), pBuffer, nBufLen); + + rtl_digest_updateMD5(&(ctx->m_hash), ctx->m_opad, 64); + rtl_digest_updateMD5(&(ctx->m_hash), pBuffer, nBufLen); + rtl_digest_getMD5(&(ctx->m_hash), pBuffer, nBufLen); + + opadHMAC_MD5(ctx); + ipadHMAC_MD5(ctx); + opadHMAC_MD5(ctx); + + return rtl_Digest_E_None; +} + +void SAL_CALL rtl_digest_destroyHMAC_MD5(rtlDigest Digest) SAL_THROW_EXTERN_C() +{ + DigestHMAC_MD5_Impl *pImpl = static_cast< DigestHMAC_MD5_Impl* >(Digest); + if (pImpl) + { + if (pImpl->m_digest.m_algorithm == rtl_Digest_AlgorithmHMAC_MD5) + rtl_freeZeroMemory(pImpl, sizeof(DigestHMAC_MD5_Impl)); + else + free(pImpl); + } +} + +#define DIGEST_CBLOCK_HMAC_SHA1 64 + +namespace { + +struct ContextHMAC_SHA1 +{ + DigestSHA_Impl m_hash; + sal_uInt8 m_opad[DIGEST_CBLOCK_HMAC_SHA1]; +}; + +struct DigestHMAC_SHA1_Impl +{ + Digest_Impl m_digest; + ContextHMAC_SHA1 m_context; +}; + +} + +static void initHMAC_SHA1(ContextHMAC_SHA1 * ctx); +static void ipadHMAC_SHA1(ContextHMAC_SHA1 * ctx); +static void opadHMAC_SHA1(ContextHMAC_SHA1 * ctx); + +const Digest_Impl HMAC_SHA1 = +{ + rtl_Digest_AlgorithmHMAC_SHA1, + RTL_DIGEST_LENGTH_SHA1, + rtl_digest_initHMAC_SHA1, + rtl_digest_destroyHMAC_SHA1, + rtl_digest_updateHMAC_SHA1, + rtl_digest_getHMAC_SHA1 +}; + +static void initHMAC_SHA1(ContextHMAC_SHA1 * ctx) +{ + DigestSHA_Impl *pImpl = &(ctx->m_hash); + + pImpl->m_digest = SHA_1; + initSHA(&(pImpl->m_context), updateSHA_1); + + memset(ctx->m_opad, 0, DIGEST_CBLOCK_HMAC_SHA1); +} + +static void ipadHMAC_SHA1(ContextHMAC_SHA1 * ctx) +{ + sal_uInt32 i; + + for (i = 0; i < DIGEST_CBLOCK_HMAC_SHA1; i++) + { + ctx->m_opad[i] ^= 0x36; + } + + rtl_digest_updateSHA1(&(ctx->m_hash), ctx->m_opad, DIGEST_CBLOCK_HMAC_SHA1); + + for (i = 0; i < DIGEST_CBLOCK_HMAC_SHA1; i++) + { + ctx->m_opad[i] ^= 0x36; + } +} + +static void opadHMAC_SHA1(ContextHMAC_SHA1 * ctx) +{ + sal_uInt32 i; + + for (i = 0; i < DIGEST_CBLOCK_HMAC_SHA1; i++) + { + ctx->m_opad[i] ^= 0x5c; + } +} + +rtlDigestError SAL_CALL rtl_digest_HMAC_SHA1( + const sal_uInt8 *pKeyData, sal_uInt32 nKeyLen, + const void *pData, sal_uInt32 nDatLen, + sal_uInt8 *pBuffer, sal_uInt32 nBufLen) SAL_THROW_EXTERN_C() +{ + DigestHMAC_SHA1_Impl digest; + rtlDigestError result; + + digest.m_digest = HMAC_SHA1; + + result = rtl_digest_initHMAC_SHA1(&digest, pKeyData, nKeyLen); + if (result == rtl_Digest_E_None) + { + result = rtl_digest_updateHMAC_SHA1(&digest, pData, nDatLen); + if (result == rtl_Digest_E_None) + result = rtl_digest_getHMAC_SHA1(&digest, pBuffer, nBufLen); + } + + rtl_secureZeroMemory(&digest, sizeof(digest)); + return result; +} + +rtlDigest SAL_CALL rtl_digest_createHMAC_SHA1() SAL_THROW_EXTERN_C() +{ + DigestHMAC_SHA1_Impl *pImpl = RTL_DIGEST_CREATE(DigestHMAC_SHA1_Impl); + if (pImpl) + { + pImpl->m_digest = HMAC_SHA1; + initHMAC_SHA1(&(pImpl->m_context)); + } + return static_cast<rtlDigest>(pImpl); +} + +rtlDigestError SAL_CALL rtl_digest_initHMAC_SHA1( + rtlDigest Digest, const sal_uInt8 *pKeyData, sal_uInt32 nKeyLen) + SAL_THROW_EXTERN_C() +{ + DigestHMAC_SHA1_Impl *pImpl = static_cast<DigestHMAC_SHA1_Impl*>(Digest); + ContextHMAC_SHA1 *ctx; + + if (!pImpl || !pKeyData) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmHMAC_SHA1) + return rtl_Digest_E_Algorithm; + + ctx = &(pImpl->m_context); + initHMAC_SHA1(ctx); + + if (nKeyLen > DIGEST_CBLOCK_HMAC_SHA1) + { + /* Initialize 'opad' with hashed 'KeyData' */ + rtl_digest_updateSHA1(&(ctx->m_hash), pKeyData, nKeyLen); + rtl_digest_getSHA1(&(ctx->m_hash), ctx->m_opad, RTL_DIGEST_LENGTH_SHA1); + } + else + { + /* Initialize 'opad' with plain 'KeyData' */ + memcpy(ctx->m_opad, pKeyData, nKeyLen); + } + + ipadHMAC_SHA1(ctx); + opadHMAC_SHA1(ctx); + + return rtl_Digest_E_None; +} + +rtlDigestError SAL_CALL rtl_digest_updateHMAC_SHA1( + rtlDigest Digest, const void *pData, sal_uInt32 nDatLen) + SAL_THROW_EXTERN_C() +{ + DigestHMAC_SHA1_Impl *pImpl = static_cast<DigestHMAC_SHA1_Impl*>(Digest); + ContextHMAC_SHA1 *ctx; + + if (!pImpl || !pData) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmHMAC_SHA1) + return rtl_Digest_E_Algorithm; + + ctx = &(pImpl->m_context); + rtl_digest_updateSHA1(&(ctx->m_hash), pData, nDatLen); + + return rtl_Digest_E_None; +} + +rtlDigestError SAL_CALL rtl_digest_getHMAC_SHA1( + rtlDigest Digest, sal_uInt8 *pBuffer, sal_uInt32 nBufLen) + SAL_THROW_EXTERN_C() +{ + DigestHMAC_SHA1_Impl *pImpl = static_cast<DigestHMAC_SHA1_Impl*>(Digest); + ContextHMAC_SHA1 *ctx; + + if (!pImpl || !pBuffer) + return rtl_Digest_E_Argument; + + if (pImpl->m_digest.m_algorithm != rtl_Digest_AlgorithmHMAC_SHA1) + return rtl_Digest_E_Algorithm; + + if (pImpl->m_digest.m_length > nBufLen) + return rtl_Digest_E_BufferSize; + + nBufLen = pImpl->m_digest.m_length; + + ctx = &(pImpl->m_context); + rtl_digest_getSHA1(&(ctx->m_hash), pBuffer, nBufLen); + + rtl_digest_updateSHA1(&(ctx->m_hash), ctx->m_opad, sizeof(ctx->m_opad)); + rtl_digest_updateSHA1(&(ctx->m_hash), pBuffer, nBufLen); + rtl_digest_getSHA1(&(ctx->m_hash), pBuffer, nBufLen); + + opadHMAC_SHA1(ctx); + ipadHMAC_SHA1(ctx); + opadHMAC_SHA1(ctx); + + return rtl_Digest_E_None; +} + +void SAL_CALL rtl_digest_destroyHMAC_SHA1(rtlDigest Digest) + SAL_THROW_EXTERN_C() +{ + DigestHMAC_SHA1_Impl *pImpl = static_cast<DigestHMAC_SHA1_Impl*>(Digest); + if (pImpl) + { + if (pImpl->m_digest.m_algorithm == rtl_Digest_AlgorithmHMAC_SHA1) + rtl_freeZeroMemory(pImpl, sizeof(DigestHMAC_SHA1_Impl)); + else + free(pImpl); + } +} + +#define DIGEST_CBLOCK_PBKDF2 RTL_DIGEST_LENGTH_HMAC_SHA1 + +static void updatePBKDF2( + rtlDigest hDigest, + sal_uInt8 T[DIGEST_CBLOCK_PBKDF2], + const sal_uInt8 *pSaltData, sal_uInt32 nSaltLen, + sal_uInt32 nCount, sal_uInt32 nIndex) +{ + /* T_i = F (P, S, c, i) */ + sal_uInt8 U[DIGEST_CBLOCK_PBKDF2]; + sal_uInt32 i, k; + + /* U_(1) = PRF (P, S || INDEX) */ + rtl_digest_updateHMAC_SHA1(hDigest, pSaltData, nSaltLen); + rtl_digest_updateHMAC_SHA1(hDigest, &nIndex, sizeof(nIndex)); + rtl_digest_getHMAC_SHA1(hDigest, U, DIGEST_CBLOCK_PBKDF2); + + /* T = U_(1) */ + for (k = 0; k < DIGEST_CBLOCK_PBKDF2; k++) + { + T[k] = U[k]; + } + + /* T ^= U_(2) ^ ... ^ U_(c) */ + for (i = 1; i < nCount; i++) + { + /* U_(i) = PRF (P, U_(i-1)) */ + rtl_digest_updateHMAC_SHA1(hDigest, U, DIGEST_CBLOCK_PBKDF2); + rtl_digest_getHMAC_SHA1(hDigest, U, DIGEST_CBLOCK_PBKDF2); + + /* T ^= U_(i) */ + for (k = 0; k < DIGEST_CBLOCK_PBKDF2; k++) + { + T[k] ^= U[k]; + } + } + + rtl_secureZeroMemory(U, DIGEST_CBLOCK_PBKDF2); +} + +rtlDigestError SAL_CALL rtl_digest_PBKDF2( + sal_uInt8 *pKeyData , sal_uInt32 nKeyLen, + const sal_uInt8 *pPassData, sal_uInt32 nPassLen, + const sal_uInt8 *pSaltData, sal_uInt32 nSaltLen, + sal_uInt32 nCount) SAL_THROW_EXTERN_C() +{ + DigestHMAC_SHA1_Impl digest; + sal_uInt32 i = 1; + + if (!pKeyData || !pPassData || !pSaltData) + return rtl_Digest_E_Argument; + + digest.m_digest = HMAC_SHA1; + rtl_digest_initHMAC_SHA1(&digest, pPassData, nPassLen); + + /* DK = T_(1) || T_(2) || ... || T_(l) */ + while (nKeyLen >= DIGEST_CBLOCK_PBKDF2) + { + /* T_(i) = F (P, S, c, i); DK ||= T_(i) */ + updatePBKDF2( + &digest, pKeyData, + pSaltData, nSaltLen, + nCount, OSL_NETDWORD(i)); + + /* Next 'KeyData' block */ + pKeyData += DIGEST_CBLOCK_PBKDF2; + nKeyLen -= DIGEST_CBLOCK_PBKDF2; + i += 1; + } + + if (nKeyLen > 0) + { + /* Last 'KeyData' block */ + sal_uInt8 T[DIGEST_CBLOCK_PBKDF2]; + + /* T_i = F (P, S, c, i) */ + updatePBKDF2( + &digest, T, + pSaltData, nSaltLen, + nCount, OSL_NETDWORD(i)); + + /* DK ||= T_(i) */ + memcpy(pKeyData, T, nKeyLen); + rtl_secureZeroMemory(T, DIGEST_CBLOCK_PBKDF2); + } + + rtl_secureZeroMemory(&digest, sizeof(digest)); + return rtl_Digest_E_None; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/hash.cxx b/sal/rtl/hash.cxx new file mode 100644 index 000000000..4fed60889 --- /dev/null +++ b/sal/rtl/hash.cxx @@ -0,0 +1,241 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <stdlib.h> + +#include "hash.hxx" +#include "strimp.hxx" +#include <osl/diagnose.h> +#include <sal/macros.h> + +namespace { + +struct StringHashTableImpl { + sal_uInt32 nEntries; + sal_uInt32 nSize; + rtl_uString **pData; +}; + +} + +typedef StringHashTableImpl StringHashTable; + +// Only for use in the implementation +static StringHashTable *rtl_str_hash_new(sal_uInt32 nSize); +static void rtl_str_hash_free(StringHashTable *pHash); + +static StringHashTable * getHashTable() +{ + static StringHashTable* pInternPool = rtl_str_hash_new(1024); + return pInternPool; +} + +// Better / smaller / faster hash set... + +// TODO: add bottom bit-set list terminator to string list + +static sal_uInt32 getNextSize(sal_uInt32 nSize) +{ + // Sedgewick - Algorithms in C P577. + static const sal_uInt32 nPrimes[] = { 1021, 2039, 4093, 8191, 16381, 32749, + 65521, 131071,262139, 524287, 1048573, + 2097143, 4194301, 8388593, 16777213, + 33554393, 67108859, 134217689 }; + + for (sal_uInt32 nPrime : nPrimes) + { + if (nPrime > nSize) + return nPrime; + } + return nSize * 2; +} + +static sal_uInt32 hashString(rtl_uString *pString) +{ + return static_cast<sal_uInt32>(rtl_ustr_hashCode_WithLength(pString->buffer, + pString->length)); +} + +static StringHashTable * rtl_str_hash_new(sal_uInt32 nSize) +{ + StringHashTable *pHash = static_cast<StringHashTable *>(malloc(sizeof(StringHashTable))); + + pHash->nEntries = 0; + pHash->nSize = getNextSize (nSize); + pHash->pData = static_cast< rtl_uString ** >(calloc(sizeof(rtl_uString *), pHash->nSize)); + + return pHash; +} + +static void rtl_str_hash_free(StringHashTable *pHash) +{ + if (!pHash) + return; + + if (pHash->pData) + free(pHash->pData); + + free(pHash); +} + +static void +rtl_str_hash_insert_nonequal(StringHashTable *pHash, + rtl_uString *pString) +{ + sal_uInt32 nHash = hashString(pString); + sal_uInt32 n; + + n = nHash % pHash->nSize; + while (pHash->pData[n]) + { + n++; + if (n >= pHash->nSize) + n = 0; + } + pHash->pData[n] = pString; +} + +static void rtl_str_hash_resize(sal_uInt32 nNewSize) +{ + sal_uInt32 i; + StringHashTable *pNewHash; + StringHashTable *pHash = getHashTable(); + + OSL_ASSERT(nNewSize > pHash->nEntries); + + pNewHash = rtl_str_hash_new(nNewSize); + + for (i = 0; i < pHash->nSize; i++) + { + if (pHash->pData[i]) + rtl_str_hash_insert_nonequal(pNewHash, pHash->pData[i]); + } + + pNewHash->nEntries = pHash->nEntries; + free(pHash->pData); + *pHash = *pNewHash; + pNewHash->pData = nullptr; + rtl_str_hash_free(pNewHash); +} + +static bool compareEqual(rtl_uString *pStringA, rtl_uString *pStringB) +{ + if (pStringA == pStringB) + return true; + + if (pStringA->length != pStringB->length) + return false; + + return !rtl_ustr_compare_WithLength( pStringA->buffer, pStringA->length, + pStringB->buffer, pStringB->length); +} + +rtl_uString * rtl_str_hash_intern ( + rtl_uString *pString, + int can_return) +{ + sal_uInt32 nHash = hashString(pString); + sal_uInt32 n; + rtl_uString *pHashStr; + + StringHashTable *pHash = getHashTable(); + + // Should we resize ? + if (pHash->nEntries >= pHash->nSize/2) + rtl_str_hash_resize(getNextSize(pHash->nSize)); + + n = nHash % pHash->nSize; + while ((pHashStr = pHash->pData[n])) + { + if (compareEqual(pHashStr, pString)) + { + rtl_uString_acquire(pHashStr); + return pHashStr; + } + + n++; + if (n >= pHash->nSize) + n = 0; + } + + if (!can_return) + { + rtl_uString *pCopy = nullptr; + rtl_uString_newFromString( &pCopy, pString ); + pString = pCopy; + + if (!pString) + return nullptr; + } + + if (!SAL_STRING_IS_STATIC(pString)) + pString->refCount |= SAL_STRING_INTERN_FLAG; + + pHash->pData[n] = pString; + pHash->nEntries++; + + return pString; +} + +void rtl_str_hash_remove(rtl_uString *pString) +{ + sal_uInt32 n; + sal_uInt32 nHash = hashString(pString); + rtl_uString *pHashStr; + + StringHashTable *pHash = getHashTable(); + + n = nHash % pHash->nSize; + while ((pHashStr = pHash->pData[n])) + { + if (compareEqual(pHashStr, pString)) + break; + + n++; + + if (n >= pHash->nSize) + n = 0; + } + + OSL_ASSERT(pHash->pData[n]); + if (!pHash->pData[n]) + return; + + pHash->pData[n++] = nullptr; + pHash->nEntries--; + + if (n >= pHash->nSize) + n = 0; + + while ((pHashStr = pHash->pData[n])) + { + pHash->pData[n] = nullptr; + // FIXME: rather unsophisticated and N^2 in chain-length, but robust. + rtl_str_hash_insert_nonequal(pHash, pHashStr); + n++; + + if (n >= pHash->nSize) + n = 0; + } + // FIXME: Should we down-size ? +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/hash.hxx b/sal/rtl/hash.hxx new file mode 100644 index 000000000..9f80448e7 --- /dev/null +++ b/sal/rtl/hash.hxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SAL_RTL_HASH_HXX +#define INCLUDED_SAL_RTL_HASH_HXX + +#include <rtl/ustring.h> + +/* These functions are not multi-thread safe: */ + +rtl_uString *rtl_str_hash_intern (rtl_uString *pString, + int can_return); +void rtl_str_hash_remove (rtl_uString *pString); + +#endif // INCLUDED_SAL_RTL_HASH_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/locale.cxx b/sal/rtl/locale.cxx new file mode 100644 index 000000000..742041ac1 --- /dev/null +++ b/sal/rtl/locale.cxx @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <stdlib.h> +#include <rtl/locale.h> + +#include <osl/diagnose.h> + +#include <rtllifecycle.h> +#include <memory> +#include <unordered_map> + +namespace { + +struct locale_deleter +{ + void operator() (rtl_Locale* p) noexcept + { + rtl_uString_release(p->Language); + rtl_uString_release(p->Country); + rtl_uString_release(p->Variant); + delete p; + } +}; + +} + +using locale_unique_ptr = std::unique_ptr<rtl_Locale, locale_deleter>; + +static std::unordered_map<sal_Int32, locale_unique_ptr> g_aLocaleTable; + +static rtl_Locale* g_pDefaultLocale = nullptr; + +void rtl_locale_init() +{ +} + +void rtl_locale_fini() +{ + g_aLocaleTable.clear(); + g_pDefaultLocale = nullptr; +} + +rtl_Locale * SAL_CALL rtl_locale_register(const sal_Unicode * language, const sal_Unicode * country, const sal_Unicode * variant) +{ + sal_Unicode c = 0; + rtl_uString* sLanguage = nullptr; + rtl_uString* sCountry = nullptr; + rtl_uString* sVariant = nullptr; + sal_Int32 hashCode = -1; + + if (!country) + country = &c; + + if (!variant) + variant = &c; + + ensureLocaleSingleton(); + + hashCode = rtl_ustr_hashCode(language) ^ rtl_ustr_hashCode(country) ^ rtl_ustr_hashCode(variant); + + auto it = g_aLocaleTable.find(hashCode); + if (it != g_aLocaleTable.end()) + return it->second.get(); + + rtl_uString_newFromStr(&sLanguage, language); + rtl_uString_newFromStr(&sCountry, country); + rtl_uString_newFromStr(&sVariant, variant); + + locale_unique_ptr newLocale(new rtl_Locale); + + newLocale->Language = sLanguage; + newLocale->Country = sCountry; + newLocale->Variant = sVariant; + newLocale->HashCode = hashCode; + + auto ret = newLocale.get(); + g_aLocaleTable.insert(it, std::pair<sal_Int32, locale_unique_ptr>( hashCode, std::move(newLocale) ) ); + return ret; +} + +rtl_Locale * SAL_CALL rtl_locale_getDefault() +{ + return g_pDefaultLocale; +} + +void SAL_CALL rtl_locale_setDefault(const sal_Unicode * language, const sal_Unicode * country, const sal_Unicode * variant) +{ + g_pDefaultLocale = rtl_locale_register(language, country, variant); +} + +rtl_uString * SAL_CALL rtl_locale_getLanguage(rtl_Locale * This) +{ + rtl_uString_acquire(This->Language); + return This->Language; +} + +rtl_uString * SAL_CALL rtl_locale_getCountry(rtl_Locale * This) +{ + rtl_uString_acquire(This->Country); + return This->Country; +} + +rtl_uString * SAL_CALL rtl_locale_getVariant(rtl_Locale * This) +{ + rtl_uString_acquire(This->Variant); + return This->Variant; +} + +sal_Int32 SAL_CALL rtl_locale_hashCode(rtl_Locale * This) +{ + return This->HashCode; +} + +sal_Int32 SAL_CALL rtl_locale_equals(rtl_Locale * This, rtl_Locale * obj) +{ + return This == obj; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/math.cxx b/sal/rtl/math.cxx new file mode 100644 index 000000000..0d70e9718 --- /dev/null +++ b/sal/rtl/math.cxx @@ -0,0 +1,828 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <rtl/math.h> + +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <rtl/character.hxx> +#include <rtl/math.hxx> + +#include <algorithm> +#include <cassert> +#include <cfenv> +#include <cmath> +#include <float.h> +#include <limits> +#include <limits.h> +#include <math.h> +#include <memory> +#include <stdlib.h> + +#include "strtmpl.hxx" + +#include <dtoa.h> + +constexpr int minExp = -323, maxExp = 308; +constexpr double n10s[] = { + 1e-323, 1e-322, 1e-321, 1e-320, 1e-319, 1e-318, 1e-317, 1e-316, 1e-315, 1e-314, 1e-313, 1e-312, + 1e-311, 1e-310, 1e-309, 1e-308, 1e-307, 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, + 1e-299, 1e-298, 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290, 1e-289, 1e-288, + 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281, 1e-280, 1e-279, 1e-278, 1e-277, 1e-276, + 1e-275, 1e-274, 1e-273, 1e-272, 1e-271, 1e-270, 1e-269, 1e-268, 1e-267, 1e-266, 1e-265, 1e-264, + 1e-263, 1e-262, 1e-261, 1e-260, 1e-259, 1e-258, 1e-257, 1e-256, 1e-255, 1e-254, 1e-253, 1e-252, + 1e-251, 1e-250, 1e-249, 1e-248, 1e-247, 1e-246, 1e-245, 1e-244, 1e-243, 1e-242, 1e-241, 1e-240, + 1e-239, 1e-238, 1e-237, 1e-236, 1e-235, 1e-234, 1e-233, 1e-232, 1e-231, 1e-230, 1e-229, 1e-228, + 1e-227, 1e-226, 1e-225, 1e-224, 1e-223, 1e-222, 1e-221, 1e-220, 1e-219, 1e-218, 1e-217, 1e-216, + 1e-215, 1e-214, 1e-213, 1e-212, 1e-211, 1e-210, 1e-209, 1e-208, 1e-207, 1e-206, 1e-205, 1e-204, + 1e-203, 1e-202, 1e-201, 1e-200, 1e-199, 1e-198, 1e-197, 1e-196, 1e-195, 1e-194, 1e-193, 1e-192, + 1e-191, 1e-190, 1e-189, 1e-188, 1e-187, 1e-186, 1e-185, 1e-184, 1e-183, 1e-182, 1e-181, 1e-180, + 1e-179, 1e-178, 1e-177, 1e-176, 1e-175, 1e-174, 1e-173, 1e-172, 1e-171, 1e-170, 1e-169, 1e-168, + 1e-167, 1e-166, 1e-165, 1e-164, 1e-163, 1e-162, 1e-161, 1e-160, 1e-159, 1e-158, 1e-157, 1e-156, + 1e-155, 1e-154, 1e-153, 1e-152, 1e-151, 1e-150, 1e-149, 1e-148, 1e-147, 1e-146, 1e-145, 1e-144, + 1e-143, 1e-142, 1e-141, 1e-140, 1e-139, 1e-138, 1e-137, 1e-136, 1e-135, 1e-134, 1e-133, 1e-132, + 1e-131, 1e-130, 1e-129, 1e-128, 1e-127, 1e-126, 1e-125, 1e-124, 1e-123, 1e-122, 1e-121, 1e-120, + 1e-119, 1e-118, 1e-117, 1e-116, 1e-115, 1e-114, 1e-113, 1e-112, 1e-111, 1e-110, 1e-109, 1e-108, + 1e-107, 1e-106, 1e-105, 1e-104, 1e-103, 1e-102, 1e-101, 1e-100, 1e-99, 1e-98, 1e-97, 1e-96, + 1e-95, 1e-94, 1e-93, 1e-92, 1e-91, 1e-90, 1e-89, 1e-88, 1e-87, 1e-86, 1e-85, 1e-84, + 1e-83, 1e-82, 1e-81, 1e-80, 1e-79, 1e-78, 1e-77, 1e-76, 1e-75, 1e-74, 1e-73, 1e-72, + 1e-71, 1e-70, 1e-69, 1e-68, 1e-67, 1e-66, 1e-65, 1e-64, 1e-63, 1e-62, 1e-61, 1e-60, + 1e-59, 1e-58, 1e-57, 1e-56, 1e-55, 1e-54, 1e-53, 1e-52, 1e-51, 1e-50, 1e-49, 1e-48, + 1e-47, 1e-46, 1e-45, 1e-44, 1e-43, 1e-42, 1e-41, 1e-40, 1e-39, 1e-38, 1e-37, 1e-36, + 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30, 1e-29, 1e-28, 1e-27, 1e-26, 1e-25, 1e-24, + 1e-23, 1e-22, 1e-21, 1e-20, 1e-19, 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, + 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, + 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, + 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23, 1e24, + 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, + 1e37, 1e38, 1e39, 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, + 1e49, 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, 1e60, + 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, 1e70, 1e71, 1e72, + 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, 1e80, 1e81, 1e82, 1e83, 1e84, + 1e85, 1e86, 1e87, 1e88, 1e89, 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, + 1e97, 1e98, 1e99, 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, + 1e109, 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, 1e120, + 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, 1e130, 1e131, 1e132, + 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 1e140, 1e141, 1e142, 1e143, 1e144, + 1e145, 1e146, 1e147, 1e148, 1e149, 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, + 1e157, 1e158, 1e159, 1e160, 1e161, 1e162, 1e163, 1e164, 1e165, 1e166, 1e167, 1e168, + 1e169, 1e170, 1e171, 1e172, 1e173, 1e174, 1e175, 1e176, 1e177, 1e178, 1e179, 1e180, + 1e181, 1e182, 1e183, 1e184, 1e185, 1e186, 1e187, 1e188, 1e189, 1e190, 1e191, 1e192, + 1e193, 1e194, 1e195, 1e196, 1e197, 1e198, 1e199, 1e200, 1e201, 1e202, 1e203, 1e204, + 1e205, 1e206, 1e207, 1e208, 1e209, 1e210, 1e211, 1e212, 1e213, 1e214, 1e215, 1e216, + 1e217, 1e218, 1e219, 1e220, 1e221, 1e222, 1e223, 1e224, 1e225, 1e226, 1e227, 1e228, + 1e229, 1e230, 1e231, 1e232, 1e233, 1e234, 1e235, 1e236, 1e237, 1e238, 1e239, 1e240, + 1e241, 1e242, 1e243, 1e244, 1e245, 1e246, 1e247, 1e248, 1e249, 1e250, 1e251, 1e252, + 1e253, 1e254, 1e255, 1e256, 1e257, 1e258, 1e259, 1e260, 1e261, 1e262, 1e263, 1e264, + 1e265, 1e266, 1e267, 1e268, 1e269, 1e270, 1e271, 1e272, 1e273, 1e274, 1e275, 1e276, + 1e277, 1e278, 1e279, 1e280, 1e281, 1e282, 1e283, 1e284, 1e285, 1e286, 1e287, 1e288, + 1e289, 1e290, 1e291, 1e292, 1e293, 1e294, 1e295, 1e296, 1e297, 1e298, 1e299, 1e300, + 1e301, 1e302, 1e303, 1e304, 1e305, 1e306, 1e307, 1e308, +}; +static_assert(SAL_N_ELEMENTS(n10s) == maxExp - minExp + 1); + +// return pow(10.0,nExp) optimized for exponents in the interval [-323,308] (i.e., incl. denormals) +static double getN10Exp(int nExp) +{ + if (nExp < minExp || nExp > maxExp) + return pow(10.0, static_cast<double>(nExp)); // will return 0 or INF with IEEE 754 + return n10s[nExp - minExp]; +} + +namespace { + +/** If value (passed as absolute value) is an integer representable as double, + which we handle explicitly at some places. + */ +bool isRepresentableInteger(double fAbsValue) +{ + assert(fAbsValue >= 0.0); + const sal_Int64 kMaxInt = (static_cast< sal_Int64 >(1) << 53) - 1; + if (fAbsValue <= static_cast< double >(kMaxInt)) + { + sal_Int64 nInt = static_cast< sal_Int64 >(fAbsValue); + // Check the integer range again because double comparison may yield + // true within the precision range. + // XXX loplugin:fpcomparison complains about floating-point comparison + // for static_cast<double>(nInt) == fAbsValue, though we actually want + // this here. + if (nInt > kMaxInt) + return false; + double fInt = static_cast< double >(nInt); + return !(fInt < fAbsValue) && !(fInt > fAbsValue); + } + return false; +} + +// Returns 1-based index of least significant bit in a number, or zero if number is zero +int findFirstSetBit(unsigned n) +{ +#if defined _WIN32 + unsigned long pos; + unsigned char bNonZero = _BitScanForward(&pos, n); + return (bNonZero == 0) ? 0 : pos + 1; +#else + return __builtin_ffs(n); +#endif +} + +/** Returns number of binary bits for fractional part of the number + Expects a proper non-negative double value, not +-INF, not NAN + */ +int getBitsInFracPart(double fAbsValue) +{ + assert(std::isfinite(fAbsValue) && fAbsValue >= 0.0); + if (fAbsValue == 0.0) + return 0; + auto pValParts = reinterpret_cast< const sal_math_Double * >(&fAbsValue); + int nExponent = pValParts->inf_parts.exponent - 1023; + if (nExponent >= 52) + return 0; // All bits in fraction are in integer part of the number + int nLeastSignificant = findFirstSetBit(pValParts->inf_parts.fraction_lo); + if (nLeastSignificant == 0) + { + nLeastSignificant = findFirstSetBit(pValParts->inf_parts.fraction_hi); + if (nLeastSignificant == 0) + nLeastSignificant = 53; // the implied leading 1 is the least significant + else + nLeastSignificant += 32; + } + int nFracSignificant = 53 - nLeastSignificant; + int nBitsInFracPart = nFracSignificant - nExponent; + + return std::max(nBitsInFracPart, 0); +} + +} + +void SAL_CALL rtl_math_doubleToString(rtl_String ** pResult, + sal_Int32 * pResultCapacity, + sal_Int32 nResultOffset, double fValue, + rtl_math_StringFormat eFormat, + sal_Int32 nDecPlaces, + char cDecSeparator, + sal_Int32 const * pGroups, + char cGroupSeparator, + sal_Bool bEraseTrailingDecZeros) + SAL_THROW_EXTERN_C() +{ + rtl::str::doubleToString( + pResult, pResultCapacity, nResultOffset, fValue, eFormat, nDecPlaces, + cDecSeparator, pGroups, cGroupSeparator, bEraseTrailingDecZeros); +} + +void SAL_CALL rtl_math_doubleToUString(rtl_uString ** pResult, + sal_Int32 * pResultCapacity, + sal_Int32 nResultOffset, double fValue, + rtl_math_StringFormat eFormat, + sal_Int32 nDecPlaces, + sal_Unicode cDecSeparator, + sal_Int32 const * pGroups, + sal_Unicode cGroupSeparator, + sal_Bool bEraseTrailingDecZeros) + SAL_THROW_EXTERN_C() +{ + rtl::str::doubleToString( + pResult, pResultCapacity, nResultOffset, fValue, eFormat, nDecPlaces, + cDecSeparator, pGroups, cGroupSeparator, bEraseTrailingDecZeros); +} + +namespace { + +template< typename CharT > +double stringToDouble(CharT const * pBegin, CharT const * pEnd, + CharT cDecSeparator, CharT cGroupSeparator, + rtl_math_ConversionStatus * pStatus, + CharT const ** pParsedEnd) +{ + double fVal = 0.0; + rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok; + + CharT const * p0 = pBegin; + while (p0 != pEnd && (*p0 == CharT(' ') || *p0 == CharT('\t'))) + { + ++p0; + } + + bool bSign; + bool explicitSign = false; + if (p0 != pEnd && *p0 == CharT('-')) + { + bSign = true; + explicitSign = true; + ++p0; + } + else + { + bSign = false; + if (p0 != pEnd && *p0 == CharT('+')) + { + explicitSign = true; + ++p0; + } + } + + CharT const * p = p0; + bool bDone = false; + + // #i112652# XMLSchema-2 + if ((pEnd - p) >= 3) + { + if (!explicitSign && (CharT('N') == p[0]) && (CharT('a') == p[1]) + && (CharT('N') == p[2])) + { + p += 3; + fVal = std::numeric_limits<double>::quiet_NaN(); + bDone = true; + } + else if ((CharT('I') == p[0]) && (CharT('N') == p[1]) + && (CharT('F') == p[2])) + { + p += 3; + fVal = HUGE_VAL; + eStatus = rtl_math_ConversionStatus_OutOfRange; + bDone = true; + } + } + + if (!bDone) // do not recognize e.g. NaN1.23 + { + std::unique_ptr<char[]> bufInHeap; + std::unique_ptr<const CharT * []> bufInHeapMap; + constexpr int bufOnStackSize = 256; + char bufOnStack[bufOnStackSize]; + const CharT* bufOnStackMap[bufOnStackSize]; + char* buf = bufOnStack; + const CharT** bufmap = bufOnStackMap; + int bufpos = 0; + const size_t bufsize = pEnd - p + (bSign ? 2 : 1); + if (bufsize > bufOnStackSize) + { + bufInHeap = std::make_unique<char[]>(bufsize); + bufInHeapMap = std::make_unique<const CharT*[]>(bufsize); + buf = bufInHeap.get(); + bufmap = bufInHeapMap.get(); + } + + if (bSign) + { + buf[0] = '-'; + bufmap[0] = p; // yes, this may be the same pointer as for the next mapping + bufpos = 1; + } + // Put first zero to buffer for strings like "-0" + if (p != pEnd && *p == CharT('0')) + { + buf[bufpos] = '0'; + bufmap[bufpos] = p; + ++bufpos; + ++p; + } + // Leading zeros and group separators between digits may be safely + // ignored. p0 < p implies that there was a leading 0 already, + // consecutive group separators may not happen as *(p+1) is checked for + // digit. + while (p != pEnd && (*p == CharT('0') || (*p == cGroupSeparator + && p0 < p && p+1 < pEnd && rtl::isAsciiDigit(*(p+1))))) + { + ++p; + } + + // integer part of mantissa + for (; p != pEnd; ++p) + { + CharT c = *p; + if (rtl::isAsciiDigit(c)) + { + buf[bufpos] = static_cast<char>(c); + bufmap[bufpos] = p; + ++bufpos; + } + else if (c != cGroupSeparator) + { + break; + } + else if (p == p0 || (p+1 == pEnd) || !rtl::isAsciiDigit(*(p+1))) + { + // A leading or trailing (not followed by a digit) group + // separator character is not a group separator. + break; + } + } + + // fraction part of mantissa + if (p != pEnd && *p == cDecSeparator) + { + buf[bufpos] = '.'; + bufmap[bufpos] = p; + ++bufpos; + ++p; + + for (; p != pEnd; ++p) + { + CharT c = *p; + if (!rtl::isAsciiDigit(c)) + { + break; + } + buf[bufpos] = static_cast<char>(c); + bufmap[bufpos] = p; + ++bufpos; + } + } + + // Exponent + if (p != p0 && p != pEnd && (*p == CharT('E') || *p == CharT('e'))) + { + buf[bufpos] = 'E'; + bufmap[bufpos] = p; + ++bufpos; + ++p; + if (p != pEnd && *p == CharT('-')) + { + buf[bufpos] = '-'; + bufmap[bufpos] = p; + ++bufpos; + ++p; + } + else if (p != pEnd && *p == CharT('+')) + ++p; + + for (; p != pEnd; ++p) + { + CharT c = *p; + if (!rtl::isAsciiDigit(c)) + break; + + buf[bufpos] = static_cast<char>(c); + bufmap[bufpos] = p; + ++bufpos; + } + } + else if (p - p0 == 2 && p != pEnd && p[0] == CharT('#') + && p[-1] == cDecSeparator && p[-2] == CharT('1')) + { + if (pEnd - p >= 4 && p[1] == CharT('I') && p[2] == CharT('N') + && p[3] == CharT('F')) + { + // "1.#INF", "+1.#INF", "-1.#INF" + p += 4; + fVal = HUGE_VAL; + eStatus = rtl_math_ConversionStatus_OutOfRange; + // Eat any further digits: + while (p != pEnd && rtl::isAsciiDigit(*p)) + ++p; + bDone = true; + } + else if (pEnd - p >= 4 && p[1] == CharT('N') && p[2] == CharT('A') + && p[3] == CharT('N')) + { + // "1.#NAN", "+1.#NAN", "-1.#NAN" + p += 4; + fVal = std::copysign(std::numeric_limits<double>::quiet_NaN(), bSign ? -1.0 : 1.0); + bSign = false; // don't negate again + + // Eat any further digits: + while (p != pEnd && rtl::isAsciiDigit(*p)) + { + ++p; + } + bDone = true; + } + } + + if (!bDone) + { + buf[bufpos] = '\0'; + bufmap[bufpos] = p; + char* pCharParseEnd; + errno = 0; + fVal = strtod_nolocale(buf, &pCharParseEnd); + if (errno == ERANGE) + { + // Check for the dreaded rounded to 15 digits max value + // 1.79769313486232e+308 for 1.7976931348623157e+308 we wrote + // everywhere, accept with or without plus sign in exponent. + const char* b = buf; + if (b[0] == '-') + ++b; + if (((pCharParseEnd - b == 21) || (pCharParseEnd - b == 20)) + && !strncmp( b, "1.79769313486232", 16) + && (b[16] == 'e' || b[16] == 'E') + && (((pCharParseEnd - b == 21) && !strncmp( b+17, "+308", 4)) + || ((pCharParseEnd - b == 20) && !strncmp( b+17, "308", 3)))) + { + fVal = (buf < b) ? -DBL_MAX : DBL_MAX; + } + else + { + eStatus = rtl_math_ConversionStatus_OutOfRange; + } + } + p = bufmap[pCharParseEnd - buf]; + bSign = false; + } + } + + // overflow also if more than DBL_MAX_10_EXP digits without decimal + // separator, or 0. and more than DBL_MIN_10_EXP digits, ... + bool bHuge = fVal == HUGE_VAL; // g++ 3.0.1 requires it this way... + if (bHuge) + eStatus = rtl_math_ConversionStatus_OutOfRange; + + if (bSign) + fVal = -fVal; + + if (pStatus) + *pStatus = eStatus; + + if (pParsedEnd) + *pParsedEnd = p == p0 ? pBegin : p; + + return fVal; +} + +} + +double SAL_CALL rtl_math_stringToDouble(char const * pBegin, + char const * pEnd, + char cDecSeparator, + char cGroupSeparator, + rtl_math_ConversionStatus * pStatus, + char const ** pParsedEnd) + SAL_THROW_EXTERN_C() +{ + return stringToDouble( + reinterpret_cast<unsigned char const *>(pBegin), + reinterpret_cast<unsigned char const *>(pEnd), + static_cast<unsigned char>(cDecSeparator), + static_cast<unsigned char>(cGroupSeparator), pStatus, + reinterpret_cast<unsigned char const **>(pParsedEnd)); +} + +double SAL_CALL rtl_math_uStringToDouble(sal_Unicode const * pBegin, + sal_Unicode const * pEnd, + sal_Unicode cDecSeparator, + sal_Unicode cGroupSeparator, + rtl_math_ConversionStatus * pStatus, + sal_Unicode const ** pParsedEnd) + SAL_THROW_EXTERN_C() +{ + return stringToDouble(pBegin, pEnd, cDecSeparator, cGroupSeparator, pStatus, + pParsedEnd); +} + +double SAL_CALL rtl_math_round(double fValue, int nDecPlaces, + enum rtl_math_RoundingMode eMode) + SAL_THROW_EXTERN_C() +{ + if (!std::isfinite(fValue)) + return fValue; + + if (fValue == 0.0) + return fValue; + + if (nDecPlaces == 0) + { + switch (eMode) + { + case rtl_math_RoundingMode_Corrected: + return std::round(fValue); + case rtl_math_RoundingMode_HalfEven: + if (const int oldMode = std::fegetround(); std::fesetround(FE_TONEAREST) == 0) + { + fValue = std::nearbyint(fValue); + std::fesetround(oldMode); + return fValue; + } + break; + default: + break; + } + } + + const double fOrigValue = fValue; + + // sign adjustment + bool bSign = std::signbit( fValue ); + if (bSign) + fValue = -fValue; + + // Rounding to decimals between integer distance precision (gaps) does not + // make sense, do not even try to multiply/divide and introduce inaccuracy. + // For same reasons, do not attempt to round integers to decimals. + if (nDecPlaces >= 0 + && (fValue >= 0x1p52 + || isRepresentableInteger(fValue))) + return fOrigValue; + + double fFac = 0; + if (nDecPlaces != 0) + { + if (nDecPlaces > 0) + { + // Determine how many decimals are representable in the precision. + // Anything greater 2^52 and 0.0 was already ruled out above. + // Theoretically 0.5, 0.25, 0.125, 0.0625, 0.03125, ... + const sal_math_Double* pd = reinterpret_cast<const sal_math_Double*>(&fValue); + const sal_Int32 nDec = 52 - (pd->parts.exponent - 1023); + + if (nDec <= 0) + { + assert(!"Shouldn't this had been caught already as large number?"); + return fOrigValue; + } + + if (nDec < nDecPlaces) + nDecPlaces = nDec; + } + + // Avoid 1e-5 (1.0000000000000001e-05) and such inaccurate fractional + // factors that later when dividing back spoil things. For negative + // decimals divide first with the inverse, then multiply the rounded + // value back. + fFac = getN10Exp(abs(nDecPlaces)); + + if (fFac == 0.0 || (nDecPlaces < 0 && !std::isfinite(fFac))) + // Underflow, rounding to that many integer positions would be 0. + return 0.0; + + if (!std::isfinite(fFac)) + // Overflow with very small values and high number of decimals. + return fOrigValue; + + if (nDecPlaces < 0) + fValue /= fFac; + else + fValue *= fFac; + + if (!std::isfinite(fValue)) + return fOrigValue; + } + + // Round only if not already in distance precision gaps of integers, where + // for [2^52,2^53) adding 0.5 would even yield the next representable + // integer. + if (fValue < 0x1p52) + { + switch ( eMode ) + { + case rtl_math_RoundingMode_Corrected : + fValue = rtl::math::approxFloor(fValue + 0.5); + break; + case rtl_math_RoundingMode_Down: + fValue = rtl::math::approxFloor(fValue); + break; + case rtl_math_RoundingMode_Up: + fValue = rtl::math::approxCeil(fValue); + break; + case rtl_math_RoundingMode_Floor: + fValue = bSign ? rtl::math::approxCeil(fValue) + : rtl::math::approxFloor( fValue ); + break; + case rtl_math_RoundingMode_Ceiling: + fValue = bSign ? rtl::math::approxFloor(fValue) + : rtl::math::approxCeil(fValue); + break; + case rtl_math_RoundingMode_HalfDown : + { + double f = floor(fValue); + fValue = ((fValue - f) <= 0.5) ? f : ceil(fValue); + } + break; + case rtl_math_RoundingMode_HalfUp: + { + double f = floor(fValue); + fValue = ((fValue - f) < 0.5) ? f : ceil(fValue); + } + break; + case rtl_math_RoundingMode_HalfEven: +#if defined FLT_ROUNDS + /* + Use fast version. FLT_ROUNDS may be defined to a function by some compilers! + + DBL_EPSILON is the smallest fractional number which can be represented, + its reciprocal is therefore the smallest number that cannot have a + fractional part. Once you add this reciprocal to `x', its fractional part + is stripped off. Simply subtracting the reciprocal back out returns `x' + without its fractional component. + Simple, clever, and elegant - thanks to Ross Cottrell, the original author, + who placed it into public domain. + + volatile: prevent compiler from being too smart + */ + if (FLT_ROUNDS == 1) + { + volatile double x = fValue + 1.0 / DBL_EPSILON; + fValue = x - 1.0 / DBL_EPSILON; + } + else +#endif // FLT_ROUNDS + { + double f = floor(fValue); + if ((fValue - f) != 0.5) + { + fValue = floor( fValue + 0.5 ); + } + else + { + double g = f / 2.0; + fValue = (g == floor( g )) ? f : (f + 1.0); + } + } + break; + default: + OSL_ASSERT(false); + break; + } + } + + if (nDecPlaces != 0) + { + if (nDecPlaces < 0) + fValue *= fFac; + else + fValue /= fFac; + } + + if (!std::isfinite(fValue)) + return fOrigValue; + + return bSign ? -fValue : fValue; +} + +double SAL_CALL rtl_math_pow10Exp(double fValue, int nExp) SAL_THROW_EXTERN_C() +{ + return fValue * getN10Exp(nExp); +} + +double SAL_CALL rtl_math_approxValue( double fValue ) SAL_THROW_EXTERN_C() +{ + const double fBigInt = 0x1p41; // 2^41 -> only 11 bits left for fractional part, fine as decimal + if (fValue == 0.0 || fValue == HUGE_VAL || !std::isfinite( fValue) || fValue > fBigInt) + { + // We don't handle these conditions. Bail out. + return fValue; + } + + double fOrigValue = fValue; + + bool bSign = std::signbit(fValue); + if (bSign) + fValue = -fValue; + + // If the value is either integer representable as double, + // or only has small number of bits in fraction part, then we need not do any approximation + if (isRepresentableInteger(fValue) || getBitsInFracPart(fValue) <= 11) + return fOrigValue; + + int nExp = static_cast< int >(floor(log10(fValue))); + nExp = 14 - nExp; + double fExpValue = getN10Exp(abs(nExp)); + + if (nExp < 0) + fValue /= fExpValue; + else + fValue *= fExpValue; + + // If the original value was near DBL_MIN we got an overflow. Restore and + // bail out. + if (!std::isfinite(fValue)) + return fOrigValue; + + fValue = std::round(fValue); + + if (nExp < 0) + fValue *= fExpValue; + else + fValue /= fExpValue; + + // If the original value was near DBL_MAX we got an overflow. Restore and + // bail out. + if (!std::isfinite(fValue)) + return fOrigValue; + + return bSign ? -fValue : fValue; +} + +bool SAL_CALL rtl_math_approxEqual(double a, double b) SAL_THROW_EXTERN_C() +{ + static const double e48 = 0x1p-48; + static const double e44 = 0x1p-44; + + if (a == b) + return true; + + if (a == 0.0 || b == 0.0) + return false; + + const double d = fabs(a - b); + if (!std::isfinite(d)) + return false; // Nan or Inf involved + + a = fabs(a); + if (d > (a * e44)) + return false; + b = fabs(b); + if (d > (b * e44)) + return false; + + if (isRepresentableInteger(d) && isRepresentableInteger(a) && isRepresentableInteger(b)) + return false; // special case for representable integers. + + return (d < a * e48 && d < b * e48); +} + +double SAL_CALL rtl_math_expm1(double fValue) SAL_THROW_EXTERN_C() +{ + return expm1(fValue); +} + +double SAL_CALL rtl_math_log1p(double fValue) SAL_THROW_EXTERN_C() +{ +#ifdef __APPLE__ + if (fValue == -0.0) + return fValue; // macOS 10.8 libc returns 0.0 for -0.0 +#endif + + return log1p(fValue); +} + +double SAL_CALL rtl_math_atanh(double fValue) SAL_THROW_EXTERN_C() +{ + return ::atanh(fValue); +} + +/** Parent error function (erf) */ +double SAL_CALL rtl_math_erf(double x) SAL_THROW_EXTERN_C() +{ + return erf(x); +} + +/** Parent complementary error function (erfc) */ +double SAL_CALL rtl_math_erfc(double x) SAL_THROW_EXTERN_C() +{ + return erfc(x); +} + +/** improved accuracy of asinh for |x| large and for x near zero + @see #i97605# + */ +double SAL_CALL rtl_math_asinh(double fX) SAL_THROW_EXTERN_C() +{ + if ( fX == 0.0 ) + return 0.0; + + double fSign = 1.0; + if ( fX < 0.0 ) + { + fX = - fX; + fSign = -1.0; + } + + if ( fX < 0.125 ) + return fSign * rtl_math_log1p( fX + fX*fX / (1.0 + sqrt( 1.0 + fX*fX))); + + if ( fX < 1.25e7 ) + return fSign * log( fX + sqrt( 1.0 + fX*fX)); + + return fSign * log( 2.0*fX); +} + +/** improved accuracy of acosh for x large and for x near 1 + @see #i97605# + */ +double SAL_CALL rtl_math_acosh(double fX) SAL_THROW_EXTERN_C() +{ + volatile double fZ = fX - 1.0; + if (fX < 1.0) + return std::numeric_limits<double>::quiet_NaN(); + if ( fX == 1.0 ) + return 0.0; + + if ( fX < 1.1 ) + return rtl_math_log1p( fZ + sqrt( fZ*fZ + 2.0*fZ)); + + if ( fX < 1.25e7 ) + return log( fX + sqrt( fX*fX - 1.0)); + + return log( 2.0*fX); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/random.cxx b/sal/rtl/random.cxx new file mode 100644 index 000000000..418358b22 --- /dev/null +++ b/sal/rtl/random.cxx @@ -0,0 +1,310 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cmath> + +#include <sal/types.h> +#include <o3tl/temporary.hxx> +#include <osl/thread.h> +#include <osl/thread.hxx> +#include <osl/time.h> +#include <rtl/alloc.h> +#include <rtl/digest.h> +#include <rtl/random.h> +#include <oslrandom.h> + +#define RTL_RANDOM_RNG_1(a) ((a) * 16807L) +#define RTL_RANDOM_RNG_2(a) ((a) * 65539L) + +#define RTL_RANDOM_RNG(x, y, z) \ +{ \ + (x) = 170 * ((x) % 178) - 63 * ((x) / 178); \ + if ((x) < 0) (x) += 30328; \ + \ + (y) = 171 * ((y) % 177) - 2 * ((y) / 177); \ + if ((y) < 0) (y) += 30269; \ + \ + (z) = 172 * ((z) % 176) - 35 * ((z) / 176); \ + if ((z) < 0) (z) += 30307; \ +} + +namespace { + +struct RandomData_Impl +{ + sal_Int16 m_nX; + sal_Int16 m_nY; + sal_Int16 m_nZ; +}; + +} + +static double data (RandomData_Impl *pImpl); + +#define RTL_RANDOM_DIGEST rtl_Digest_AlgorithmMD5 +#define RTL_RANDOM_SIZE_DIGEST RTL_DIGEST_LENGTH_MD5 +#define RTL_RANDOM_SIZE_POOL 1023 + +namespace { + +struct RandomPool_Impl +{ + rtlDigest m_hDigest; + sal_uInt8 m_pDigest[RTL_RANDOM_SIZE_DIGEST]; + sal_uInt8 m_pData[RTL_RANDOM_SIZE_POOL + 1]; + sal_uInt32 m_nData; + sal_uInt32 m_nIndex; + sal_uInt32 m_nCount; +}; + +} + +static bool initPool(RandomPool_Impl *pImpl); + +static void seedPool( + RandomPool_Impl *pImpl, const sal_uInt8 *pBuffer, sal_Size nBufLen); + +static void readPool( + RandomPool_Impl *pImpl, sal_uInt8 *pBuffer, sal_Size nBufLen); + +static double data(RandomData_Impl *pImpl) +{ + double random; + + RTL_RANDOM_RNG (pImpl->m_nX, pImpl->m_nY, pImpl->m_nZ); + random = ((static_cast<double>(pImpl->m_nX) / 30328.0) + + (static_cast<double>(pImpl->m_nY) / 30269.0) + + (static_cast<double>(pImpl->m_nZ) / 30307.0) ); + + return std::modf(random, &o3tl::temporary(double())); +} + +static bool initPool(RandomPool_Impl *pImpl) +{ + pImpl->m_hDigest = rtl_digest_create(RTL_RANDOM_DIGEST); + if (pImpl->m_hDigest) + { + oslThreadIdentifier tid; + TimeValue tv; + RandomData_Impl rd; + double seed; + + /* The use of uninitialized stack variables as a way to + * enhance the entropy of the random pool triggers + * memory checkers like purify and valgrind. + */ + + /* + seedPool (pImpl, (sal_uInt8*)&tid, sizeof(tid)); + seedPool (pImpl, (sal_uInt8*)&tv, sizeof(tv)); + seedPool (pImpl, (sal_uInt8*)&rd, sizeof(rd)); + */ + + tid = osl::Thread::getCurrentIdentifier(); + tid = RTL_RANDOM_RNG_2(RTL_RANDOM_RNG_1(tid)); + seedPool (pImpl, reinterpret_cast< sal_uInt8* >(&tid), sizeof(tid)); + + osl_getSystemTime (&tv); + tv.Seconds = RTL_RANDOM_RNG_2(tv.Seconds); + tv.Nanosec = RTL_RANDOM_RNG_2(tv.Nanosec); + seedPool (pImpl, reinterpret_cast< sal_uInt8* >(&tv), sizeof(tv)); + + rd.m_nX = static_cast<sal_Int16>(((tid >> 1) << 1) + 1); + rd.m_nY = static_cast<sal_Int16>(((tv.Seconds >> 1) << 1) + 1); + rd.m_nZ = static_cast<sal_Int16>(((tv.Nanosec >> 1) << 1) + 1); + seedPool (pImpl, reinterpret_cast< sal_uInt8* >(&rd), sizeof(rd)); + + while (pImpl->m_nData < RTL_RANDOM_SIZE_POOL) + { + seed = data (&rd); + seedPool (pImpl, reinterpret_cast< sal_uInt8* >(&seed), sizeof(seed)); + } + return true; + } + return false; +} + +static void seedPool( + RandomPool_Impl *pImpl, const sal_uInt8 *pBuffer, sal_Size nBufLen) +{ + sal_Size i; + sal_sSize j, k; + + for (i = 0; i < nBufLen; i += RTL_RANDOM_SIZE_DIGEST) + { + j = nBufLen - i; + if (j > RTL_RANDOM_SIZE_DIGEST) + j = RTL_RANDOM_SIZE_DIGEST; + + rtl_digest_update( + pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST); + + k = (pImpl->m_nIndex + j) - RTL_RANDOM_SIZE_POOL; + if (k > 0) + { + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_pData[pImpl->m_nIndex]), j - k); + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_pData[0]), k); + } + else + { + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_pData[pImpl->m_nIndex]), j); + } + + rtl_digest_update(pImpl->m_hDigest, pBuffer, j); + pBuffer += j; + + rtl_digest_get( + pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST); + for (k = 0; k < j; k++) + { + pImpl->m_pData[pImpl->m_nIndex++] ^= pImpl->m_pDigest[k]; + if (pImpl->m_nIndex >= RTL_RANDOM_SIZE_POOL) + { + pImpl->m_nData = RTL_RANDOM_SIZE_POOL; + pImpl->m_nIndex = 0; + } + } + } + + if (pImpl->m_nIndex > pImpl->m_nData) + pImpl->m_nData = pImpl->m_nIndex; +} + +static void readPool ( + RandomPool_Impl *pImpl, sal_uInt8 *pBuffer, sal_Size nBufLen) +{ + sal_Int32 j, k; + + while (nBufLen > 0) + { + j = nBufLen; + if (j > RTL_RANDOM_SIZE_DIGEST/2) + j = RTL_RANDOM_SIZE_DIGEST/2; + nBufLen -= j; + + rtl_digest_update( + pImpl->m_hDigest, + &(pImpl->m_pDigest[RTL_RANDOM_SIZE_DIGEST/2]), + RTL_RANDOM_SIZE_DIGEST/2); + + k = (pImpl->m_nIndex + j) - pImpl->m_nData; + if (k > 0) + { + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_pData[pImpl->m_nIndex]), j - k); + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_pData[0]), k); + } + else + { + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_pData[pImpl->m_nIndex]), j); + } + + rtl_digest_get( + pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST); + for (k = 0; k < j; k++) + { + if (pImpl->m_nIndex >= pImpl->m_nData) + pImpl->m_nIndex = 0; + + pImpl->m_pData[pImpl->m_nIndex++] ^= pImpl->m_pDigest[k]; + *pBuffer++ = pImpl->m_pDigest[k + RTL_RANDOM_SIZE_DIGEST/2]; + } + } + + pImpl->m_nCount++; + rtl_digest_update( + pImpl->m_hDigest, &(pImpl->m_nCount), sizeof(pImpl->m_nCount)); + rtl_digest_update( + pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST); + rtl_digest_get( + pImpl->m_hDigest, pImpl->m_pDigest, RTL_RANDOM_SIZE_DIGEST); +} + +rtlRandomPool SAL_CALL rtl_random_createPool() SAL_THROW_EXTERN_C() +{ + /* try to get system random number, if it fail fall back on own pool */ + RandomPool_Impl *pImpl = static_cast< RandomPool_Impl* >(rtl_allocateZeroMemory(sizeof(RandomPool_Impl))); + if (pImpl) + { + char sanity[4]; + if (!osl_get_system_random_data(sanity, 4)) + { + if (!initPool(pImpl)) + { + rtl_freeZeroMemory(pImpl, sizeof(RandomPool_Impl)); + pImpl = nullptr; + } + } + } + return static_cast< rtlRandomPool >(pImpl); +} + +void SAL_CALL rtl_random_destroyPool(rtlRandomPool Pool) SAL_THROW_EXTERN_C() +{ + RandomPool_Impl *pImpl = static_cast< RandomPool_Impl* >(Pool); + if (pImpl) + { + if (pImpl->m_hDigest) + rtl_digest_destroy(pImpl->m_hDigest); + + rtl_freeZeroMemory (pImpl, sizeof(RandomPool_Impl)); + } +} + +rtlRandomError SAL_CALL rtl_random_addBytes( + rtlRandomPool Pool, const void *Buffer, sal_Size Bytes) SAL_THROW_EXTERN_C() +{ + RandomPool_Impl *pImpl = static_cast< RandomPool_Impl* >(Pool); + const sal_uInt8 *pBuffer = static_cast< const sal_uInt8* >(Buffer); + + if (!pImpl || !pBuffer) + return rtl_Random_E_Argument; + + if (pImpl->m_hDigest) + seedPool (pImpl, pBuffer, Bytes); + + return rtl_Random_E_None; +} + +rtlRandomError SAL_CALL rtl_random_getBytes ( + rtlRandomPool Pool, void *Buffer, sal_Size Bytes) SAL_THROW_EXTERN_C() +{ + RandomPool_Impl *pImpl = static_cast< RandomPool_Impl* >(Pool); + sal_uInt8 *pBuffer = static_cast< sal_uInt8* >(Buffer); + + if (!pImpl || !pBuffer) + return rtl_Random_E_Argument; + + if (pImpl->m_hDigest || !osl_get_system_random_data(static_cast< char* >(Buffer), Bytes)) + { + if (!pImpl->m_hDigest && !initPool(pImpl)) + return rtl_Random_E_Unknown; + readPool(pImpl, pBuffer, Bytes); + } + return rtl_Random_E_None; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/rtl_process.cxx b/sal/rtl/rtl_process.cxx new file mode 100644 index 000000000..76963dc13 --- /dev/null +++ b/sal/rtl/rtl_process.cxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cstring> + +#include <rtl/process.h> +#include <rtl/uuid.h> +#include <sal/types.h> + +namespace +{ + +class Id +{ +public: + Id() { rtl_createUuid(uuid_, nullptr, false); } + + Id(const Id&) = delete; + Id& operator=(const Id&) = delete; + + void copy(sal_uInt8 * target) const + { std::memcpy(target, uuid_, UUID_SIZE); } + +private: + enum { UUID_SIZE = 16 }; + + sal_uInt8 uuid_[UUID_SIZE]; +}; + +} // end namespace + +void rtl_getGlobalProcessId(sal_uInt8 * pTargetUUID) +{ + static Id theId; + theId.copy(pTargetUUID); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/strbuf.cxx b/sal/rtl/strbuf.cxx new file mode 100644 index 000000000..7ba25ff4a --- /dev/null +++ b/sal/rtl/strbuf.cxx @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <string.h> + +#include <osl/interlck.h> +#include <rtl/strbuf.hxx> +#include "strimp.hxx" + +#if USE_SDT_PROBES +#define RTL_LOG_STRING_BITS 8 +#endif + +#include "strtmpl.hxx" + +/************************************************************************* + * rtl_stringbuffer_newFromStr_WithLength + */ +void SAL_CALL rtl_stringbuffer_newFromStr_WithLength( rtl_String ** newStr, + const char * value, + sal_Int32 count ) +{ + rtl::str::stringbuffer_newFromStr_WithLength(newStr, value, count); +} + +/************************************************************************* + * rtl_stringbuffer_newFromStringBuffer + */ +sal_Int32 SAL_CALL rtl_stringbuffer_newFromStringBuffer( rtl_String ** newStr, + sal_Int32 capacity, + rtl_String * oldStr ) +{ + return rtl::str::stringbuffer_newFromStringBuffer(newStr, capacity, oldStr); +} + +/************************************************************************* + * rtl_stringbuffer_ensureCapacity + */ +void SAL_CALL rtl_stringbuffer_ensureCapacity + (rtl_String ** This, sal_Int32* capacity, sal_Int32 minimumCapacity) +{ + rtl::str::stringbuffer_ensureCapacity(This, capacity, minimumCapacity); +} + +/************************************************************************* + * rtl_stringbuffer_insert + */ +void SAL_CALL rtl_stringbuffer_insert( rtl_String ** This, + sal_Int32 * capacity, + sal_Int32 offset, + const char * str, + sal_Int32 len ) +{ + rtl::str::stringbuffer_insert(This, capacity, offset, str, len); +} + +/************************************************************************* + * rtl_stringbuffer_remove + */ +void SAL_CALL rtl_stringbuffer_remove( rtl_String ** This, + sal_Int32 start, + sal_Int32 len ) +{ + rtl::str::stringbuffer_remove(This, start, len); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/strimp.cxx b/sal/rtl/strimp.cxx new file mode 100644 index 000000000..44b56023d --- /dev/null +++ b/sal/rtl/strimp.cxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include <sal/config.h> + +#include <assert.h> +#include <rtl/alloc.h> +#include <rtl/ustring.h> +#include <rtllifecycle.h> + +#include "strimp.hxx" +#include "alloc_impl.hxx" +#include "alloc_arena.hxx" + +/* + * TODO: add a slower, more awful, but more space efficient + * custom allocator for the pre-init phase. Existing slab + * allocator's minimum alloc size is 24bytes, and by default + * is 32 bytes. + */ +static rtl_arena_type *pre_arena = nullptr; + +rtl_allocateStringFn rtl_allocateString = malloc; +rtl_freeStringFn rtl_freeString = free; + +extern "C" { +static void *pre_allocateStringFn(size_t n) +{ + sal_Size size = RTL_MEMORY_ALIGN(n + 4, 4); + char *addr = static_cast<char*>(rtl_arena_alloc(pre_arena, &size)); + assert(size>= 12); + reinterpret_cast<sal_uInt32*>(addr)[0] = size - 12; + return addr + 4; +} + +static void pre_freeStringFn(void *data) +{ + char *addr = static_cast<char*>(data) - 4; + sal_uInt32 size = reinterpret_cast<sal_uInt32*>(addr)[0] + 12; + + rtl_arena_free(pre_arena, addr, size); +} +} // extern "C" + +static void mark_static(void *addr, sal_Size /* size */) +{ + char *inner = static_cast<char*>(addr) + 4; + rtl_uString *str = reinterpret_cast<rtl_uString *>(inner); + str->refCount |= SAL_STRING_STATIC_FLAG; +} + +void SAL_CALL rtl_alloc_preInit (sal_Bool start) SAL_THROW_EXTERN_C() +{ + if (start) + { + rtl_allocateString = pre_allocateStringFn; + rtl_freeString = pre_freeStringFn; + pre_arena = rtl_arena_create("pre-init strings", 4, 0, + nullptr, rtl_arena_alloc, + rtl_arena_free, 0); + + // To be consistent (and to ensure the rtl_cache threads are started). + ensureCacheSingleton(); + } + else + { + rtl_arena_foreach(pre_arena, mark_static); + rtl_allocateString = malloc; + rtl_freeString = free; + + // TODO: also re-initialize main allocator as well. + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/strimp.hxx b/sal/rtl/strimp.hxx new file mode 100644 index 000000000..69404e0aa --- /dev/null +++ b/sal/rtl/strimp.hxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SAL_RTL_STRIMP_HXX +#define INCLUDED_SAL_RTL_STRIMP_HXX + +#include <config_probes.h> +#if USE_SDT_PROBES +#include <sys/sdt.h> +#endif + +#include <sal/types.h> +#include <rtl/string.hxx> +#include <rtl/ustring.hxx> + +/* ======================================================================= */ +/* Help functions for String and UString */ +/* ======================================================================= */ + +/* + * refCount is opaque and includes 2 bit-fields; + * MSB: 'interned' - is stored in the intern hash + * MSB-1: 'static' - is a const / static string, + * do no ref counting + */ +#define SAL_STRING_INTERN_FLAG 0x80000000 +#define SAL_STRING_STATIC_FLAG 0x40000000 +#define SAL_STRING_REFCOUNT(a) ((a) & 0x3fffffff) + +#define SAL_STRING_IS_INTERN(a) ((a)->refCount & SAL_STRING_INTERN_FLAG) +#define SAL_STRING_IS_STATIC(a) ((a)->refCount & SAL_STRING_STATIC_FLAG) + +rtl_uString* rtl_uString_ImplAlloc( sal_Int32 nLen ); + +rtl_String* rtl_string_ImplAlloc( sal_Int32 nLen ); + +extern "C" { + +typedef void *(SAL_CALL * rtl_allocateStringFn)(size_t size); +typedef void (*rtl_freeStringFn)(void *); + +} + +extern rtl_allocateStringFn rtl_allocateString; +extern rtl_freeStringFn rtl_freeString; + +// string lifetime instrumentation / diagnostics +#if USE_SDT_PROBES +# define PROBE_SNAME(n,b) n ## _ ## b +# define PROBE_NAME(n,b) PROBE_SNAME(n,b) +# define PROBE_NEW PROBE_NAME (new_string,RTL_LOG_STRING_BITS) +# define PROBE_DEL PROBE_NAME (delete_string,RTL_LOG_STRING_BITS) +# define PROBE_INTERN_NEW PROBE_NAME (new_string_intern,RTL_LOG_STRING_BITS) +# define PROBE_INTERN_DEL PROBE_NAME (delete_string_intern,RTL_LOG_STRING_BITS) +# define RTL_LOG_STRING_NEW(s) \ + DTRACE_PROBE4(libreoffice, PROBE_NEW, s, \ + (s)->refCount, (s)->length, (s)->buffer) +# define RTL_LOG_STRING_DELETE(s) \ + DTRACE_PROBE4(libreoffice, PROBE_DEL, s, \ + (s)->refCount, (s)->length, (s)->buffer) +# define RTL_LOG_STRING_INTERN_NEW(s,o) \ + DTRACE_PROBE5(libreoffice, PROBE_INTERN_NEW, s, \ + (s)->refCount, (s)->length, (s)->buffer, o) +# define RTL_LOG_STRING_INTERN_DELETE(s) \ + DTRACE_PROBE4(libreoffice, PROBE_INTERN_DEL, s, \ + (s)->refCount, (s)->length, (s)->buffer) +#else +# define RTL_LOG_STRING_NEW(s) +# define RTL_LOG_STRING_DELETE(s) +# define RTL_LOG_STRING_INTERN_NEW(s,o) +# define RTL_LOG_STRING_INTERN_DELETE(s) +#endif /* USE_SDT_PROBES */ + +#endif // INCLUDED_SAL_RTL_STRIMP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/string.cxx b/sal/rtl/string.cxx new file mode 100644 index 000000000..23196e528 --- /dev/null +++ b/sal/rtl/string.cxx @@ -0,0 +1,667 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <cstdlib> + +#include <osl/interlck.h> +#include <osl/diagnose.h> +#include <rtl/tencinfo.h> + +#include "strimp.hxx" +#include <rtl/character.hxx> +#include <rtl/string.h> + +#include <rtl/math.h> + +#if defined _WIN32 +// Temporary check to verify that the #pragma pack around rtl_String is indeed cargo cult and can +// safely be removed: +static_assert(alignof (rtl_String) == 4); +static_assert(sizeof (rtl_String) == 12); +#endif + +/* ======================================================================= */ + +#if USE_SDT_PROBES +#define RTL_LOG_STRING_BITS 8 +#endif + +#include "strtmpl.hxx" + +/* ======================================================================= */ + +sal_Int32 SAL_CALL rtl_str_valueOfFloat(char * pStr, float f) + SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfFP<RTL_STR_MAX_VALUEOFFLOAT>(pStr, f); +} + +sal_Int32 SAL_CALL rtl_str_valueOfDouble(char * pStr, double d) + SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfFP<RTL_STR_MAX_VALUEOFDOUBLE>(pStr, d); +} + +float SAL_CALL rtl_str_toFloat(char const * pStr) SAL_THROW_EXTERN_C() +{ + assert(pStr); + return static_cast<float>(rtl_math_stringToDouble(pStr, pStr + rtl_str_getLength(pStr), + '.', 0, nullptr, nullptr)); +} + +double SAL_CALL rtl_str_toDouble(char const * pStr) SAL_THROW_EXTERN_C() +{ + assert(pStr); + return rtl_math_stringToDouble(pStr, pStr + rtl_str_getLength(pStr), '.', 0, + nullptr, nullptr); +} + +/* ======================================================================= */ + +static int rtl_ImplGetFastUTF8ByteLen( const sal_Unicode* pStr, sal_Int32 nLen ) +{ + int n; + sal_Unicode c; + sal_uInt32 nUCS4Char; + const sal_Unicode* pEndStr; + + n = 0; + pEndStr = pStr+nLen; + while ( pStr < pEndStr ) + { + c = *pStr; + + if ( c < 0x80 ) + n++; + else if ( c < 0x800 ) + n += 2; + else + { + if ( !rtl::isHighSurrogate(c) ) + n += 3; + else + { + nUCS4Char = c; + + if ( pStr+1 < pEndStr ) + { + c = *(pStr+1); + if ( rtl::isLowSurrogate(c) ) + { + nUCS4Char = rtl::combineSurrogates(nUCS4Char, c); + pStr++; + } + } + + if ( nUCS4Char < 0x10000 ) + n += 3; + else if ( nUCS4Char < 0x200000 ) + n += 4; + else if ( nUCS4Char < 0x4000000 ) + n += 5; + else + n += 6; + } + } + + pStr++; + } + + return n; +} + +/* ----------------------------------------------------------------------- */ + +static bool rtl_impl_convertUStringToString(rtl_String ** pTarget, + sal_Unicode const * pSource, + sal_Int32 nLength, + rtl_TextEncoding nEncoding, + sal_uInt32 nFlags, + bool bCheckErrors) +{ + assert(pTarget != nullptr); + assert(pSource != nullptr || nLength == 0); + assert(nLength >= 0); + OSL_ASSERT(nLength == 0 || rtl_isOctetTextEncoding(nEncoding)); + + if ( !nLength ) + rtl_string_new( pTarget ); + else + { + rtl_String* pTemp; + rtl_UnicodeToTextConverter hConverter; + sal_uInt32 nInfo; + sal_Size nSrcChars; + sal_Size nDestBytes; + sal_Size nNewLen; + sal_Size nNotConvertedChars; + sal_Size nMaxCharLen; + + /* Optimization for UTF-8 - we try to calculate the exact length */ + /* For all other encoding we try a good estimation */ + if ( nEncoding == RTL_TEXTENCODING_UTF8 ) + { + nNewLen = rtl_ImplGetFastUTF8ByteLen( pSource, nLength ); + /* Includes the string only ASCII, then we could copy + the buffer faster */ + if ( nNewLen == static_cast<sal_Size>(nLength) ) + { + char* pBuffer; + if ( *pTarget ) + rtl_string_release( *pTarget ); + *pTarget = rtl_string_ImplAlloc( nLength ); + OSL_ASSERT(*pTarget != nullptr); + pBuffer = (*pTarget)->buffer; + do + { + /* Check ASCII range */ + OSL_ENSURE( *pSource <= 127, + "rtl_uString2String() - UTF8 test is encoding is wrong" ); + + *pBuffer = static_cast<char>(static_cast<unsigned char>(*pSource)); + pBuffer++; + pSource++; + nLength--; + } + while ( nLength ); + return true; + } + + nMaxCharLen = 4; + } + else + { + rtl_TextEncodingInfo aTextEncInfo; + aTextEncInfo.StructSize = sizeof( aTextEncInfo ); + if ( !rtl_getTextEncodingInfo( nEncoding, &aTextEncInfo ) ) + { + aTextEncInfo.AverageCharSize = 1; + aTextEncInfo.MaximumCharSize = 8; + } + + nNewLen = nLength * static_cast<sal_Size>(aTextEncInfo.AverageCharSize); + nMaxCharLen = aTextEncInfo.MaximumCharSize; + } + + nFlags |= RTL_UNICODETOTEXT_FLAGS_FLUSH; + hConverter = rtl_createUnicodeToTextConverter( nEncoding ); + + for (;;) + { + pTemp = rtl_string_ImplAlloc( nNewLen ); + OSL_ASSERT(pTemp != nullptr); + nDestBytes = rtl_convertUnicodeToText( hConverter, nullptr, + pSource, nLength, + pTemp->buffer, nNewLen, + nFlags, + &nInfo, &nSrcChars ); + if (bCheckErrors && (nInfo & RTL_UNICODETOTEXT_INFO_ERROR) != 0) + { + rtl_freeString(pTemp); + rtl_destroyUnicodeToTextConverter(hConverter); + return false; + } + + if ((nInfo & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) == 0) + break; + + /* Buffer not big enough, try again with enough space */ + rtl_freeString( pTemp ); + + /* Try with the max. count of characters with + additional overhead for replacing functionality */ + nNotConvertedChars = nLength-nSrcChars; + nNewLen = nDestBytes+(nNotConvertedChars*nMaxCharLen)+nNotConvertedChars+4; + } + + /* Set the buffer to the correct size or is there to + much overhead, reallocate to the correct size */ + if ( nNewLen > nDestBytes+8 ) + { + rtl_String* pTemp2 = rtl_string_ImplAlloc( nDestBytes ); + OSL_ASSERT(pTemp2 != nullptr); + rtl::str::Copy(pTemp2->buffer, pTemp->buffer, nDestBytes); + rtl_freeString( pTemp ); + pTemp = pTemp2; + } + else + { + pTemp->length = nDestBytes; + pTemp->buffer[nDestBytes] = 0; + } + + rtl_destroyUnicodeToTextConverter( hConverter ); + if ( *pTarget ) + rtl_string_release( *pTarget ); + *pTarget = pTemp; + + /* Results the conversion in an empty buffer - + create an empty string */ + if ( pTemp && !nDestBytes ) + rtl_string_new( pTarget ); + } + return true; +} + +void SAL_CALL rtl_uString2String( rtl_String** ppThis, + const sal_Unicode* pUStr, + sal_Int32 nULen, + rtl_TextEncoding eTextEncoding, + sal_uInt32 nCvtFlags ) + SAL_THROW_EXTERN_C() +{ + rtl_impl_convertUStringToString(ppThis, pUStr, nULen, eTextEncoding, + nCvtFlags, false); +} + +sal_Bool SAL_CALL rtl_convertUStringToString(rtl_String ** pTarget, + sal_Unicode const * pSource, + sal_Int32 nLength, + rtl_TextEncoding nEncoding, + sal_uInt32 nFlags) + SAL_THROW_EXTERN_C() +{ + return rtl_impl_convertUStringToString(pTarget, pSource, nLength, nEncoding, + nFlags, true); +} + +void rtl_string_newReplaceFirst( + rtl_String ** newStr, rtl_String * str, char const * from, + sal_Int32 fromLength, char const * to, sal_Int32 toLength, + sal_Int32 * index) SAL_THROW_EXTERN_C() +{ + assert(str != nullptr); + assert(index != nullptr); + assert(*index >= 0 && *index <= str->length); + assert(fromLength >= 0); + assert(toLength >= 0); + sal_Int32 i = rtl_str_indexOfStr_WithLength( + str->buffer + *index, str->length - *index, from, fromLength); + if (i == -1) { + rtl_string_assign(newStr, str); + } else { + assert(i <= str->length - *index); + i += *index; + assert(fromLength <= str->length); + if (str->length - fromLength > SAL_MAX_INT32 - toLength) { + std::abort(); + } + rtl::str::newReplaceStrAt(newStr, str, i, fromLength, to, toLength); + } + *index = i; +} + +void rtl_string_newReplaceAll( + rtl_String ** newStr, rtl_String * str, char const * from, + sal_Int32 fromLength, char const * to, sal_Int32 toLength) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceAllFromIndex(newStr, str, from, fromLength, to, toLength, 0); +} + +sal_Int32 SAL_CALL rtl_str_getLength(const char* pStr) SAL_THROW_EXTERN_C() +{ + return rtl::str::getLength(pStr); +} + +sal_Int32 SAL_CALL rtl_str_compare(const char* pStr1, const char* pStr2) SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::null_terminated(pStr1), rtl::str::null_terminated(pStr2), + rtl::str::CompareNormal(), rtl::str::noShortening); +} + +sal_Int32 SAL_CALL rtl_str_compare_WithLength(const char* pStr1, sal_Int32 nStr1Len, + const char* pStr2, sal_Int32 nStr2Len) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len), + rtl::str::with_length(pStr2, nStr2Len), + rtl::str::CompareNormal(), rtl::str::noShortening); +} + +sal_Int32 SAL_CALL rtl_str_shortenedCompare_WithLength(const char* pStr1, sal_Int32 nStr1Len, + const char* pStr2, sal_Int32 nStr2Len, + sal_Int32 nShortenedLength) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len), + rtl::str::with_length(pStr2, nStr2Len), + rtl::str::CompareNormal(), nShortenedLength); +} + +sal_Int32 SAL_CALL rtl_str_reverseCompare_WithLength(const char* pStr1, sal_Int32 nStr1Len, + const char* pStr2, sal_Int32 nStr2Len) + SAL_THROW_EXTERN_C() +{ + return rtl::str::reverseCompare_WithLengths(pStr1, nStr1Len, pStr2, nStr2Len, + rtl::str::CompareNormal()); +} + +sal_Int32 SAL_CALL rtl_str_compareIgnoreAsciiCase(const char* pStr1, const char* pStr2) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::null_terminated(pStr1), rtl::str::null_terminated(pStr2), + rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening); +} + +sal_Int32 SAL_CALL rtl_str_compareIgnoreAsciiCase_WithLength(const char* pStr1, sal_Int32 nStr1Len, + const char* pStr2, sal_Int32 nStr2Len) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len), + rtl::str::with_length(pStr2, nStr2Len), + rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening); +} + +sal_Int32 SAL_CALL rtl_str_shortenedCompareIgnoreAsciiCase_WithLength( + const char* pStr1, sal_Int32 nStr1Len, const char* pStr2, sal_Int32 nStr2Len, + sal_Int32 nShortenedLength) SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len), + rtl::str::with_length(pStr2, nStr2Len), + rtl::str::CompareIgnoreAsciiCase(), nShortenedLength); +} + +sal_Int32 SAL_CALL rtl_str_hashCode(const char* pStr) SAL_THROW_EXTERN_C() +{ + return rtl::str::hashCode(pStr); +} + +sal_Int32 SAL_CALL rtl_str_hashCode_WithLength(const char* pStr, sal_Int32 nLen) + SAL_THROW_EXTERN_C() +{ + return rtl::str::hashCode_WithLength(pStr, nLen); +} + +sal_Int32 SAL_CALL rtl_str_indexOfChar(const char* pStr, char c) SAL_THROW_EXTERN_C() +{ + return rtl::str::indexOfChar(pStr, c); +} + +sal_Int32 SAL_CALL rtl_str_indexOfChar_WithLength(const char* pStr, sal_Int32 nLen, char c) + SAL_THROW_EXTERN_C() +{ + return rtl::str::indexOfChar_WithLength(pStr, nLen, c); +} + +sal_Int32 SAL_CALL rtl_str_lastIndexOfChar(const char* pStr, char c) SAL_THROW_EXTERN_C() +{ + return rtl::str::lastIndexOfChar(pStr, c); +} + +sal_Int32 SAL_CALL rtl_str_lastIndexOfChar_WithLength(const char* pStr, sal_Int32 nLen, char c) + SAL_THROW_EXTERN_C() +{ + return rtl::str::lastIndexOfChar_WithLength(pStr, nLen, c); +} + +sal_Int32 SAL_CALL rtl_str_indexOfStr(const char* pStr, const char* pSubStr) SAL_THROW_EXTERN_C() +{ + return rtl::str::indexOfStr(pStr, pSubStr); +} + +sal_Int32 SAL_CALL rtl_str_indexOfStr_WithLength(const char* pStr, sal_Int32 nStrLen, + const char* pSubStr, sal_Int32 nSubLen) + SAL_THROW_EXTERN_C() +{ + return rtl::str::indexOfStr_WithLength(pStr, nStrLen, pSubStr, nSubLen); +} + +sal_Int32 SAL_CALL rtl_str_lastIndexOfStr(const char* pStr, const char* pSubStr) + SAL_THROW_EXTERN_C() +{ + return rtl::str::lastIndexOfStr(pStr, pSubStr); +} + +sal_Int32 SAL_CALL rtl_str_lastIndexOfStr_WithLength(const char* pStr, sal_Int32 nStrLen, + const char* pSubStr, sal_Int32 nSubLen) + SAL_THROW_EXTERN_C() +{ + return rtl::str::lastIndexOfStr_WithLength(pStr, nStrLen, pSubStr, nSubLen); +} + +void SAL_CALL rtl_str_replaceChar(char* pStr, char cOld, char cNew) SAL_THROW_EXTERN_C() +{ + return rtl::str::replaceChars(rtl::str::null_terminated(pStr), rtl::str::FromTo(cOld, cNew)); +} + +void SAL_CALL rtl_str_replaceChar_WithLength(char* pStr, sal_Int32 nLen, char cOld, char cNew) + SAL_THROW_EXTERN_C() +{ + return rtl::str::replaceChars(rtl::str::with_length(pStr, nLen), rtl::str::FromTo(cOld, cNew)); +} + +void SAL_CALL rtl_str_toAsciiLowerCase(char* pStr) SAL_THROW_EXTERN_C() +{ + return rtl::str::replaceChars(rtl::str::null_terminated(pStr), rtl::str::toAsciiLower); +} + +void SAL_CALL rtl_str_toAsciiLowerCase_WithLength(char* pStr, sal_Int32 nLen) SAL_THROW_EXTERN_C() +{ + return rtl::str::replaceChars(rtl::str::with_length(pStr, nLen), rtl::str::toAsciiLower); +} + +void SAL_CALL rtl_str_toAsciiUpperCase(char* pStr) SAL_THROW_EXTERN_C() +{ + return rtl::str::replaceChars(rtl::str::null_terminated(pStr), rtl::str::toAsciiUpper); +} + +void SAL_CALL rtl_str_toAsciiUpperCase_WithLength(char* pStr, sal_Int32 nLen) SAL_THROW_EXTERN_C() +{ + return rtl::str::replaceChars(rtl::str::with_length(pStr, nLen), rtl::str::toAsciiUpper); +} + +sal_Int32 SAL_CALL rtl_str_trim(char* pStr) SAL_THROW_EXTERN_C() { return rtl::str::trim(pStr); } + +sal_Int32 SAL_CALL rtl_str_trim_WithLength(char* pStr, sal_Int32 nLen) SAL_THROW_EXTERN_C() +{ + return rtl::str::trim_WithLength(pStr, nLen); +} + +sal_Int32 SAL_CALL rtl_str_valueOfBoolean(char* pStr, sal_Bool b) SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfBoolean(pStr, b); +} + +sal_Int32 SAL_CALL rtl_str_valueOfChar(char* pStr, char c) SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfChar(pStr, c); +} + +sal_Int32 SAL_CALL rtl_str_valueOfInt32(char* pStr, sal_Int32 n, sal_Int16 nRadix) + SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfInt<RTL_STR_MAX_VALUEOFINT32>(pStr, n, nRadix); +} + +sal_Int32 SAL_CALL rtl_str_valueOfInt64(char* pStr, sal_Int64 n, sal_Int16 nRadix) + SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfInt<RTL_STR_MAX_VALUEOFINT64>(pStr, n, nRadix); +} + +sal_Int32 SAL_CALL rtl_str_valueOfUInt64(char* pStr, sal_uInt64 n, sal_Int16 nRadix) + SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfInt<RTL_STR_MAX_VALUEOFUINT64>(pStr, n, nRadix); +} + +sal_Bool SAL_CALL rtl_str_toBoolean(const char* pStr) SAL_THROW_EXTERN_C() +{ + return rtl::str::toBoolean(pStr); +} + +sal_Int32 SAL_CALL rtl_str_toInt32(const char* pStr, sal_Int16 nRadix) SAL_THROW_EXTERN_C() +{ + return rtl::str::toInt<sal_Int32>(rtl::str::null_terminated(pStr), nRadix); +} + +sal_Int64 SAL_CALL rtl_str_toInt64(const char* pStr, sal_Int16 nRadix) SAL_THROW_EXTERN_C() +{ + return rtl::str::toInt<sal_Int64>(rtl::str::null_terminated(pStr), nRadix); +} + +sal_Int64 SAL_CALL rtl_str_toInt64_WithLength(const char* pStr, sal_Int16 nRadix, + sal_Int32 nStrLength) SAL_THROW_EXTERN_C() +{ + return rtl::str::toInt<sal_Int64>(rtl::str::with_length(pStr, nStrLength), nRadix); +} + +sal_uInt32 SAL_CALL rtl_str_toUInt32(const char* pStr, sal_Int16 nRadix) SAL_THROW_EXTERN_C() +{ + return rtl::str::toInt<sal_uInt32>(rtl::str::null_terminated(pStr), nRadix); +} + +sal_uInt64 SAL_CALL rtl_str_toUInt64(const char* pStr, sal_Int16 nRadix) SAL_THROW_EXTERN_C() +{ + return rtl::str::toInt<sal_uInt64>(rtl::str::null_terminated(pStr), nRadix); +} + +rtl_String* rtl_string_ImplAlloc(sal_Int32 nLen) { return rtl::str::Alloc<rtl_String>(nLen); } + +void SAL_CALL rtl_string_acquire(rtl_String* pThis) SAL_THROW_EXTERN_C() +{ + return rtl::str::acquire(pThis); +} + +void SAL_CALL rtl_string_release(rtl_String* pThis) SAL_THROW_EXTERN_C() +{ + return rtl::str::release(pThis); +} + +void SAL_CALL rtl_string_new(rtl_String** ppThis) SAL_THROW_EXTERN_C() +{ + return rtl::str::new_(ppThis); +} + +rtl_String* SAL_CALL rtl_string_alloc(sal_Int32 nLen) SAL_THROW_EXTERN_C() +{ + assert(nLen >= 0); + return rtl::str::Alloc<rtl_String>(nLen); +} + +void SAL_CALL rtl_string_new_WithLength(rtl_String** ppThis, sal_Int32 nLen) SAL_THROW_EXTERN_C() +{ + rtl::str::new_WithLength(ppThis, nLen); +} + +void SAL_CALL rtl_string_newFromString(rtl_String** ppThis, const rtl_String* pStr) + SAL_THROW_EXTERN_C() +{ + rtl::str::newFromString(ppThis, pStr); +} + +void SAL_CALL rtl_string_newFromStr(rtl_String** ppThis, const char* pCharStr) SAL_THROW_EXTERN_C() +{ + rtl::str::newFromStr(ppThis, pCharStr); +} + +void SAL_CALL rtl_string_newFromStr_WithLength(rtl_String** ppThis, const char* pCharStr, + sal_Int32 nLen) SAL_THROW_EXTERN_C() +{ + rtl::str::newFromStr_WithLength(ppThis, pCharStr, nLen); +} + +void SAL_CALL rtl_string_newFromSubString(rtl_String** ppThis, const rtl_String* pFrom, + sal_Int32 beginIndex, sal_Int32 count) + SAL_THROW_EXTERN_C() +{ + rtl::str::newFromSubString(ppThis, pFrom, beginIndex, count); +} + +// Used when creating from string literals. +void SAL_CALL rtl_string_newFromLiteral(rtl_String** ppThis, const char* pCharStr, sal_Int32 nLen, + sal_Int32 allocExtra) SAL_THROW_EXTERN_C() +{ + rtl::str::newFromStr_WithLength(ppThis, pCharStr, nLen, allocExtra); +} + +void SAL_CALL rtl_string_assign(rtl_String** ppThis, rtl_String* pStr) SAL_THROW_EXTERN_C() +{ + rtl::str::assign(ppThis, pStr); +} + +sal_Int32 SAL_CALL rtl_string_getLength(const rtl_String* pThis) SAL_THROW_EXTERN_C() +{ + return rtl::str::getLength(pThis); +} + +char* SAL_CALL rtl_string_getStr(rtl_String* pThis) SAL_THROW_EXTERN_C() +{ + return rtl::str::getStr(pThis); +} + +void SAL_CALL rtl_string_newConcat(rtl_String** ppThis, rtl_String* pLeft, rtl_String* pRight) + SAL_THROW_EXTERN_C() +{ + rtl::str::newConcat(ppThis, pLeft, pRight); +} + +void SAL_CALL rtl_string_ensureCapacity(rtl_String** ppThis, sal_Int32 size) SAL_THROW_EXTERN_C() +{ + rtl::str::ensureCapacity(ppThis, size); +} + +void SAL_CALL rtl_string_newReplaceStrAt(rtl_String** ppThis, rtl_String* pStr, sal_Int32 nIndex, + sal_Int32 nCount, rtl_String* pNewSubStr) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceStrAt(ppThis, pStr, nIndex, nCount, pNewSubStr); +} + +void SAL_CALL rtl_string_newReplaceStrAt_WithLength(rtl_String** ppThis, rtl_String* pStr, sal_Int32 nIndex, + sal_Int32 nCount, char const * subStr, sal_Int32 substrLen) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceStrAt(ppThis, pStr, nIndex, nCount, subStr, substrLen); +} + +void SAL_CALL rtl_string_newReplace(rtl_String** ppThis, rtl_String* pStr, char cOld, char cNew) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplace(ppThis, pStr, cOld, cNew); +} + +void SAL_CALL rtl_string_newToAsciiLowerCase(rtl_String** ppThis, rtl_String* pStr) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceChars<rtl::str::ToAsciiLower>(ppThis, pStr); +} + +void SAL_CALL rtl_string_newToAsciiUpperCase(rtl_String** ppThis, rtl_String* pStr) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceChars<rtl::str::ToAsciiUpper>(ppThis, pStr); +} + +void SAL_CALL rtl_string_newTrim(rtl_String** ppThis, rtl_String* pStr) SAL_THROW_EXTERN_C() +{ + rtl::str::newTrim(ppThis, pStr); +} + +sal_Int32 SAL_CALL rtl_string_getToken(rtl_String** ppThis, rtl_String* pStr, sal_Int32 nToken, + char cTok, sal_Int32 nIndex) SAL_THROW_EXTERN_C() +{ + return rtl::str::getToken(ppThis, pStr, nToken, cTok, nIndex); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/strtmpl.hxx b/sal/rtl/strtmpl.hxx new file mode 100644 index 000000000..e1389ce6a --- /dev/null +++ b/sal/rtl/strtmpl.hxx @@ -0,0 +1,1904 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <algorithm> +#include <cassert> +#include <cmath> +#include <cstdlib> +#include <cstring> +#include <cwchar> +#include <limits> +#include <string_view> +#include <type_traits> +#include <utility> + +#include "strimp.hxx" + +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <rtl/character.hxx> +#include <rtl/math.h> +#include <rtl/string.h> +#include <rtl/ustring.h> + +#include <dragonbox/dragonbox.h> + +void internRelease(rtl_uString*); + +namespace rtl::str +{ +template <typename C> auto IMPL_RTL_USTRCODE(C c) { return std::make_unsigned_t<C>(c); } + +// Wrappers around null-terminated/known-length strings, that allow to generalize algorithms +// without overhead (e.g., without need to get length of null-terminated strings). + +template <typename C> struct null_terminated +{ + C* p; + null_terminated(C* pStr) + : p(pStr) + { + assert(pStr); + } + auto begin() const { return p; } + struct EndDetector + { + friend bool operator==(EndDetector, C* iter) { return *iter == 0; } + friend bool operator==(C* iter, EndDetector) { return *iter == 0; } + friend bool operator!=(EndDetector, C* iter) { return *iter != 0; } + friend bool operator!=(C* iter, EndDetector) { return *iter != 0; } + }; + static auto end() { return EndDetector{}; } +}; + +template <typename C> struct with_length +{ + C* p; + sal_Int32 len; + with_length(C* pStr, sal_Int32 nLength) + : p(pStr) + , len(nLength) + { + assert(len >= 0); + } + auto begin() const { return p; } + auto end() const { return p + len; } +}; + +struct ToAsciiLower +{ + template <typename C> static bool Applicable(C c) + { + return rtl::isAsciiUpperCase(IMPL_RTL_USTRCODE(c)); + } + template <typename C> static C Replace(C c) + { + return rtl::toAsciiLowerCase(IMPL_RTL_USTRCODE(c)); + } +} constexpr toAsciiLower; + +struct ToAsciiUpper +{ + template <typename C> static bool Applicable(C c) + { + return rtl::isAsciiLowerCase(IMPL_RTL_USTRCODE(c)); + } + template <typename C> static C Replace(C c) + { + return rtl::toAsciiUpperCase(IMPL_RTL_USTRCODE(c)); + } +} constexpr toAsciiUpper; + +template <typename C> struct FromTo +{ + C from; + C to; + FromTo(C cFrom, C cTo) : from(cFrom), to(cTo) {} + C Replace(C c) const { return c == from ? to : c; } +}; + +template <typename C> void Copy(C* _pDest, const C* _pSrc, sal_Int32 _nCount) +{ + // take advantage of builtin optimisations + std::copy(_pSrc, _pSrc + _nCount, _pDest); +} + +template <typename C> void CopyBackward(C* _pDest, const C* _pSrc, sal_Int32 _nCount) +{ + // take advantage of builtin optimisations + std::copy_backward(_pSrc, _pSrc + _nCount, _pDest + _nCount); +} + +inline void Copy(sal_Unicode* _pDest, const char* _pSrc, sal_Int32 _nCount) +{ + std::transform(_pSrc, _pSrc + _nCount, _pDest, + [](char c) + { + assert(rtl::isAscii(static_cast<unsigned char>(c))); + SAL_WARN_IF(c == '\0', "rtl.string", "Found embedded \\0 ASCII character"); + return static_cast<unsigned char>(c); + }); +} + +inline sal_Int16 implGetDigit(sal_Unicode ch, sal_Int16 nRadix) +{ + sal_Int16 n = -1; + if ((ch >= '0') && (ch <= '9')) + n = ch - '0'; + else if ((ch >= 'a') && (ch <= 'z')) + n = ch - 'a' + 10; + else if ((ch >= 'A') && (ch <= 'Z')) + n = ch - 'A' + 10; + return (n < nRadix) ? n : -1; +} + +inline bool implIsWhitespace(sal_Unicode c) +{ + /* Space or Control character? */ + if ((c <= 32) && c) + return true; + + /* Only in the General Punctuation area Space or Control characters are included? */ + if ((c < 0x2000) || (c > 0x206F)) + return false; + + if (((c >= 0x2000) && (c <= 0x200B)) || /* All Spaces */ + (c == 0x2028) || /* LINE SEPARATOR */ + (c == 0x2029)) /* PARAGRAPH SEPARATOR */ + return true; + + return false; +} + +/* ======================================================================= */ +/* C-String functions which could be used without the String-Class */ +/* ======================================================================= */ + +template <typename T> sal_Int32 getLength( const T* pStr ) +{ + assert(pStr); + if constexpr (std::is_class_v<T>) + { + return pStr->length; + } + else + { + // take advantage of builtin optimisations + return std::char_traits<T>::length(pStr); + } +} + +/* ----------------------------------------------------------------------- */ + +template <typename C> void warnIfCharAndNotAscii(C c) +{ + if constexpr (sizeof(c) == sizeof(char)) + SAL_WARN_IF(!rtl::isAscii(static_cast<unsigned char>(c)), "rtl.string", + "Found non-ASCII char"); +} + +template <typename C1, typename C2> void warnIfOneIsCharAndNotAscii(C1 c1, C2 c2) +{ + if constexpr (sizeof(c1) != sizeof(c2)) + { + warnIfCharAndNotAscii(c1); + warnIfCharAndNotAscii(c2); + } +} + +struct CompareNormal +{ + template <typename C1, typename C2> static sal_Int32 compare(C1 c1, C2 c2) + { + warnIfOneIsCharAndNotAscii(c1, c2); + return static_cast<sal_Int32>(IMPL_RTL_USTRCODE(c1)) + - static_cast<sal_Int32>(IMPL_RTL_USTRCODE(c2)); + } +}; + +struct CompareIgnoreAsciiCase +{ + template <typename C1, typename C2> static sal_Int32 compare(C1 c1, C2 c2) + { + warnIfOneIsCharAndNotAscii(c1, c2); + return rtl::compareIgnoreAsciiCase(IMPL_RTL_USTRCODE(c1), IMPL_RTL_USTRCODE(c2)); + } +}; + +/* ----------------------------------------------------------------------- */ + +struct NoShortening +{ + constexpr bool operator>=(int) { return true; } // for assert + constexpr bool operator==(int) { return false; } // for loop break check + constexpr void operator--() {} // for decrement in loop +} constexpr noShortening; + +template <class S1, class S2, class Compare, typename Shorten_t> +sal_Int32 compare(S1 s1, S2 s2, Compare, Shorten_t shortenedLength) +{ + static_assert(std::is_same_v<Shorten_t, NoShortening> || std::is_same_v<Shorten_t, sal_Int32>); + assert(shortenedLength >= 0); + auto pStr1 = s1.begin(); + const auto end1 = s1.end(); + auto pStr2 = s2.begin(); + const auto end2 = s2.end(); + for (;;) + { + if (shortenedLength == 0) + return 0; + if (pStr2 == end2) + return pStr1 == end1 ? 0 : 1; + if (pStr1 == end1) + return -1; + if (const sal_Int32 nRet = Compare::compare(*pStr1, *pStr2)) + return nRet; + --shortenedLength; + ++pStr1; + ++pStr2; + } +} + +// take advantage of builtin optimisations +template <typename C, std::enable_if_t<sizeof(C) == sizeof(wchar_t), int> = 0> +sal_Int32 compare(null_terminated<C> s1, null_terminated<C> s2, CompareNormal, NoShortening) +{ + return wcscmp(reinterpret_cast<wchar_t const*>(s1.p), reinterpret_cast<wchar_t const*>(s2.p)); +} +template <typename C, std::enable_if_t<sizeof(C) == sizeof(char), int> = 0> +sal_Int32 compare(null_terminated<C> s1, null_terminated<C> s2, CompareNormal, NoShortening) +{ + return strcmp(reinterpret_cast<char const*>(s1.p), reinterpret_cast<char const*>(s2.p)); +} +template <typename C> +sal_Int32 compare(with_length<C> s1, with_length<C> s2, CompareNormal, NoShortening) +{ + std::basic_string_view sv1(s1.p, s1.len); + return sv1.compare(std::basic_string_view(s2.p, s2.len)); +} +template <typename C1, typename C2, class Compare> +sal_Int32 compare(with_length<C1> s1, with_length<C2> s2, Compare cf, sal_Int32 nShortenedLength) +{ + assert(nShortenedLength >= 0); + s1.len = std::min(s1.len, nShortenedLength); + s2.len = std::min(s2.len, nShortenedLength); + return compare(s1, s2, cf, noShortening); +} + +/* ----------------------------------------------------------------------- */ + +template <typename C1, typename C2, class Compare> +sal_Int32 reverseCompare_WithLengths(const C1* pStr1, sal_Int32 nStr1Len, + const C2* pStr2, sal_Int32 nStr2Len, Compare) +{ + assert(pStr1 || nStr1Len == 0); + assert(nStr1Len >= 0); + assert(pStr2 || nStr2Len == 0); + assert(nStr2Len >= 0); + const C1* pStr1Run = pStr1+nStr1Len; + const C2* pStr2Run = pStr2+nStr2Len; + while ((pStr1 < pStr1Run) && (pStr2 < pStr2Run)) + { + pStr1Run--; + pStr2Run--; + if (const sal_Int32 nRet = Compare::compare(*pStr1Run, *pStr2Run)) + return nRet; + } + + return nStr1Len - nStr2Len; +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +sal_Int32 hashCode_WithLength(const IMPL_RTL_STRCODE*, sal_Int32); + +template <typename IMPL_RTL_STRCODE> sal_Int32 hashCode( const IMPL_RTL_STRCODE* pStr ) +{ + return hashCode_WithLength( pStr, getLength( pStr ) ); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +sal_Int32 hashCode_WithLength ( const IMPL_RTL_STRCODE* pStr, + sal_Int32 nLen ) +{ + assert(nLen >= 0); + sal_uInt32 h = static_cast<sal_uInt32>(nLen); + while ( nLen > 0 ) + { + h = (h*37U) + IMPL_RTL_USTRCODE( *pStr ); + pStr++; + nLen--; + } + return static_cast<sal_Int32>(h); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +sal_Int32 indexOfChar ( const IMPL_RTL_STRCODE* pStr, + IMPL_RTL_STRCODE c ) +{ + assert(pStr); + if (!c) + return -1; // Unifies behavior of strchr/wcschr and unoptimized algorithm wrt '\0' + + if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(char)) + { + // take advantage of builtin optimisations + const IMPL_RTL_STRCODE* p = strchr(pStr, c); + return p ? p - pStr : -1; + } + else if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t)) + { + // take advantage of builtin optimisations + wchar_t const * p = wcschr(reinterpret_cast<wchar_t const *>(pStr), static_cast<wchar_t>(c)); + return p ? p - reinterpret_cast<wchar_t const *>(pStr) : -1; + } + else + { + const IMPL_RTL_STRCODE* pTempStr = pStr; + while ( *pTempStr ) + { + if ( *pTempStr == c ) + return pTempStr-pStr; + + pTempStr++; + } + + return -1; + } +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +sal_Int32 indexOfChar_WithLength ( const IMPL_RTL_STRCODE* pStr, + sal_Int32 nLen, + IMPL_RTL_STRCODE c ) +{ +// assert(nLen >= 0); + if (nLen <= 0) + return -1; + // take advantage of builtin optimisations + using my_string_view = std::basic_string_view<IMPL_RTL_STRCODE>; + my_string_view v(pStr, nLen); + typename my_string_view::size_type idx = v.find(c); + return idx == my_string_view::npos ? -1 : idx; +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +sal_Int32 lastIndexOfChar_WithLength(const IMPL_RTL_STRCODE*, sal_Int32, IMPL_RTL_STRCODE); + +template <typename IMPL_RTL_STRCODE> +sal_Int32 lastIndexOfChar ( const IMPL_RTL_STRCODE* pStr, + IMPL_RTL_STRCODE c ) +{ + assert(pStr); + if (!c) + return -1; // Unifies behavior of strrchr/wcsrchr and lastIndexOfChar_WithLength wrt '\0' + + if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(char)) + { + // take advantage of builtin optimisations + const IMPL_RTL_STRCODE* p = strrchr(pStr, c); + return p ? p - pStr : -1; + } + else if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t)) + { + // take advantage of builtin optimisations + wchar_t const * p = wcsrchr(reinterpret_cast<wchar_t const *>(pStr), static_cast<wchar_t>(c)); + return p ? p - reinterpret_cast<wchar_t const *>(pStr) : -1; + } + else + { + return lastIndexOfChar_WithLength( pStr, getLength( pStr ), c ); + } +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +sal_Int32 lastIndexOfChar_WithLength ( const IMPL_RTL_STRCODE* pStr, + sal_Int32 nLen, + IMPL_RTL_STRCODE c ) +{ + assert(nLen >= 0); + // take advantage of builtin optimisations + using my_string_view = std::basic_string_view<IMPL_RTL_STRCODE>; + my_string_view v(pStr, nLen); + typename my_string_view::size_type idx = v.rfind(c); + return idx == my_string_view::npos ? -1 : idx; +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +sal_Int32 indexOfStr_WithLength(const IMPL_RTL_STRCODE*, sal_Int32, const IMPL_RTL_STRCODE*, + sal_Int32); + +template <typename IMPL_RTL_STRCODE> +sal_Int32 indexOfStr ( const IMPL_RTL_STRCODE* pStr, + const IMPL_RTL_STRCODE* pSubStr ) +{ + assert(pStr); + assert(pSubStr); + /* an empty SubString is always not findable */ + if (*pSubStr == 0) + return -1; + if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(char)) + { + // take advantage of builtin optimisations + const IMPL_RTL_STRCODE* p = strstr(pStr, pSubStr); + return p ? p - pStr : -1; + } + else if constexpr (sizeof(IMPL_RTL_STRCODE) == sizeof(wchar_t)) + { + // take advantage of builtin optimisations + wchar_t const * p = wcsstr(reinterpret_cast<wchar_t const *>(pStr), reinterpret_cast<wchar_t const *>(pSubStr)); + return p ? p - reinterpret_cast<wchar_t const *>(pStr) : -1; + } + else + { + return indexOfStr_WithLength( pStr, getLength( pStr ), + pSubStr, getLength( pSubStr ) ); + } +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +sal_Int32 indexOfStr_WithLength ( const IMPL_RTL_STRCODE* pStr, + sal_Int32 nStrLen, + const IMPL_RTL_STRCODE* pSubStr, + sal_Int32 nSubLen ) +{ + assert(nStrLen >= 0); + assert(nSubLen >= 0); + /* an empty SubString is always not findable */ + if ( nSubLen == 0 ) + return -1; + // take advantage of builtin optimisations + using my_string_view = std::basic_string_view<IMPL_RTL_STRCODE>; + my_string_view v(pStr, nStrLen); + auto idx = nSubLen == 1 ? v.find(*pSubStr) : v.find(pSubStr, 0, nSubLen); + return idx == my_string_view::npos ? -1 : idx; +} + +inline sal_Int32 indexOfStr_WithLength(const sal_Unicode* pStr, sal_Int32 nStrLen, + const char* pSubStr, sal_Int32 nSubLen) +{ + assert(nStrLen >= 0); + assert(nSubLen >= 0); + if (nSubLen > 0 && nSubLen <= nStrLen) + { + sal_Unicode const* end = pStr + nStrLen; + sal_Unicode const* cursor = pStr; + + while (cursor < end) + { + cursor = std::char_traits<sal_Unicode>::find(cursor, end - cursor, *pSubStr); + if (!cursor || (end - cursor < nSubLen)) + { + /* no enough left to actually have a match */ + break; + } + /* now it is worth trying a full match */ + if (nSubLen == 1 || rtl_ustr_asciil_reverseEquals_WithLength(cursor, pSubStr, nSubLen)) + { + return cursor - pStr; + } + cursor += 1; + } + } + return -1; +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +sal_Int32 lastIndexOfStr_WithLength(const IMPL_RTL_STRCODE*, sal_Int32, const IMPL_RTL_STRCODE*, + sal_Int32); + +template <typename IMPL_RTL_STRCODE> +sal_Int32 lastIndexOfStr ( const IMPL_RTL_STRCODE* pStr, + const IMPL_RTL_STRCODE* pSubStr ) +{ + return lastIndexOfStr_WithLength( pStr, getLength( pStr ), + pSubStr, getLength( pSubStr ) ); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +sal_Int32 lastIndexOfStr_WithLength ( const IMPL_RTL_STRCODE* pStr, + sal_Int32 nStrLen, + const IMPL_RTL_STRCODE* pSubStr, + sal_Int32 nSubLen ) +{ + assert(nStrLen >= 0); + assert(nSubLen >= 0); + /* an empty SubString is always not findable */ + if ( nSubLen == 0 ) + return -1; + // take advantage of builtin optimisations + using my_string_view = std::basic_string_view<IMPL_RTL_STRCODE>; + my_string_view v(pStr, nStrLen); + my_string_view needle(pSubStr, nSubLen); + typename my_string_view::size_type idx = v.rfind(needle); + return idx == my_string_view::npos ? -1 : idx; +} + +/* ----------------------------------------------------------------------- */ + +template <class S, class Replacer> void replaceChars(S str, Replacer replacer) +{ + for (auto& rChar : str) + rChar = replacer.Replace(rChar); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> sal_Int32 trim_WithLength(IMPL_RTL_STRCODE*, sal_Int32); + +template <typename IMPL_RTL_STRCODE> sal_Int32 trim( IMPL_RTL_STRCODE* pStr ) +{ + return trim_WithLength( pStr, getLength( pStr ) ); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +std::basic_string_view<IMPL_RTL_STRCODE> trimView( IMPL_RTL_STRCODE* pStr, sal_Int32 nLen ) +{ + assert(nLen >= 0); + sal_Int32 nPreSpaces = 0; + sal_Int32 nPostSpaces = 0; + sal_Int32 nIndex = nLen-1; + + while ( (nPreSpaces < nLen) && implIsWhitespace( IMPL_RTL_USTRCODE(*(pStr+nPreSpaces)) ) ) + nPreSpaces++; + + while ( (nIndex > nPreSpaces) && implIsWhitespace( IMPL_RTL_USTRCODE(*(pStr+nIndex)) ) ) + { + nPostSpaces++; + nIndex--; + } + + return { pStr + nPreSpaces, static_cast<size_t>(nLen - nPostSpaces - nPreSpaces) }; +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +sal_Int32 trim_WithLength( IMPL_RTL_STRCODE* pStr, sal_Int32 nLen ) +{ + const auto view = trimView(pStr, nLen); + + if (static_cast<sal_Int32>(view.size()) != nLen) + { + nLen = static_cast<sal_Int32>(view.size()); + if (view.data() != pStr) + Copy(pStr, view.data(), nLen); + *(pStr+nLen) = 0; + } + + return nLen; +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> sal_Int32 valueOfBoolean( IMPL_RTL_STRCODE* pStr, sal_Bool b ) +{ + assert(pStr); + if ( b ) + { + *pStr = 't'; + pStr++; + *pStr = 'r'; + pStr++; + *pStr = 'u'; + pStr++; + *pStr = 'e'; + pStr++; + *pStr = 0; + return 4; + } + else + { + *pStr = 'f'; + pStr++; + *pStr = 'a'; + pStr++; + *pStr = 'l'; + pStr++; + *pStr = 's'; + pStr++; + *pStr = 'e'; + pStr++; + *pStr = 0; + return 5; + } +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> +sal_Int32 valueOfChar ( IMPL_RTL_STRCODE* pStr, + IMPL_RTL_STRCODE c ) +{ + assert(pStr); + *pStr++ = c; + *pStr = 0; + return 1; +} + +/* ----------------------------------------------------------------------- */ + +template <sal_Int32 maxLen, typename IMPL_RTL_STRCODE, typename T> +sal_Int32 valueOfInt ( IMPL_RTL_STRCODE* pStr, + T n, + sal_Int16 nRadix ) +{ + assert(pStr); + assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX ); + char aBuf[maxLen]; + char* pBuf = aBuf; + sal_Int32 nLen = 0; + using uT = std::make_unsigned_t<T>; + uT nValue; + + /* Radix must be valid */ + if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) ) + nRadix = 10; + + if constexpr (std::is_signed_v<T>) + { + /* is value negative */ + if ( n < 0 ) + { + *pStr = '-'; + pStr++; + nLen++; + nValue = n == std::numeric_limits<T>::min() ? static_cast<uT>(n) : -n; + } + else + nValue = n; + } + else + nValue = n; + + /* create a recursive buffer with all values, except the last one */ + do + { + char nDigit = static_cast<char>(nValue % nRadix); + nValue /= nRadix; + if ( nDigit > 9 ) + *pBuf = (nDigit-10) + 'a'; + else + *pBuf = (nDigit + '0' ); + pBuf++; + } + while ( nValue > 0 ); + + /* copy the values in the right direction into the destination buffer */ + do + { + pBuf--; + *pStr = *pBuf; + pStr++; + nLen++; + } + while ( pBuf != aBuf ); + *pStr = 0; + + return nLen; +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRCODE> sal_Bool toBoolean( const IMPL_RTL_STRCODE* pStr ) +{ + assert(pStr); + if ( *pStr == '1' ) + return true; + + if ( (*pStr == 'T') || (*pStr == 't') ) + { + pStr++; + if ( (*pStr == 'R') || (*pStr == 'r') ) + { + pStr++; + if ( (*pStr == 'U') || (*pStr == 'u') ) + { + pStr++; + if ( (*pStr == 'E') || (*pStr == 'e') ) + return true; + } + } + } + + return false; +} + +/* ----------------------------------------------------------------------- */ + +template <typename T, class Iter> inline bool HandleSignChar(Iter& iter) +{ + if constexpr (std::numeric_limits<T>::is_signed) + { + if (*iter == '-') + { + ++iter; + return true; + } + } + if (*iter == '+') + ++iter; + return false; +} + +template <typename T> std::pair<T, sal_Int16> DivMod(sal_Int16 nRadix, [[maybe_unused]] bool bNeg) +{ + if constexpr (std::numeric_limits<T>::is_signed) + if (bNeg) + return { -(std::numeric_limits<T>::min() / nRadix), + -(std::numeric_limits<T>::min() % nRadix) }; + return { std::numeric_limits<T>::max() / nRadix, std::numeric_limits<T>::max() % nRadix }; +} + +template <typename T, class S> T toInt(S str, sal_Int16 nRadix) +{ + assert( nRadix >= RTL_STR_MIN_RADIX && nRadix <= RTL_STR_MAX_RADIX ); + + if ( (nRadix < RTL_STR_MIN_RADIX) || (nRadix > RTL_STR_MAX_RADIX) ) + nRadix = 10; + + auto pStr = str.begin(); + const auto end = str.end(); + + /* Skip whitespaces */ + while (pStr != end && implIsWhitespace(IMPL_RTL_USTRCODE(*pStr))) + pStr++; + if (pStr == end) + return 0; + + const bool bNeg = HandleSignChar<T>(pStr); + const auto& [nDiv, nMod] = DivMod<T>(nRadix, bNeg); + assert(nDiv > 0); + + std::make_unsigned_t<T> n = 0; + while (pStr != end) + { + sal_Int16 nDigit = implGetDigit(IMPL_RTL_USTRCODE(*pStr), nRadix); + if ( nDigit < 0 ) + break; + if (static_cast<std::make_unsigned_t<T>>(nMod < nDigit ? nDiv - 1 : nDiv) < n) + return 0; + + n *= nRadix; + n += nDigit; + + pStr++; + } + + if constexpr (std::numeric_limits<T>::is_signed) + if (bNeg) + return n == static_cast<std::make_unsigned_t<T>>(std::numeric_limits<T>::min()) + ? std::numeric_limits<T>::min() + : -static_cast<T>(n); + return static_cast<T>(n); +} + +/* ======================================================================= */ +/* Internal String-Class help functions */ +/* ======================================================================= */ + +template <class STRINGDATA> using STRCODE = std::remove_extent_t<decltype(STRINGDATA::buffer)>; +template <typename C> struct STRINGDATA_; +template <> struct STRINGDATA_<char> +{ + using T = rtl_String; +}; +template <> struct STRINGDATA_<sal_Unicode> +{ + using T = rtl_uString; +}; +template <typename C> using STRINGDATA = typename STRINGDATA_<C>::T; + +template <typename IMPL_RTL_STRINGDATA> IMPL_RTL_STRINGDATA* Alloc( sal_Int32 nLen ) +{ + IMPL_RTL_STRINGDATA * pData + = (sal::static_int_cast< sal_uInt32 >(nLen) + <= ((SAL_MAX_UINT32 - sizeof (IMPL_RTL_STRINGDATA)) + / sizeof (STRCODE<IMPL_RTL_STRINGDATA>))) + ? static_cast<IMPL_RTL_STRINGDATA *>(rtl_allocateString( + sizeof (IMPL_RTL_STRINGDATA) + nLen * sizeof (STRCODE<IMPL_RTL_STRINGDATA>))) + : nullptr; + if (pData != nullptr) { + pData->refCount = 1; + pData->length = nLen; + pData->buffer[nLen] = 0; + } + return pData; +} + +/* ======================================================================= */ +/* String-Class functions */ +/* ======================================================================= */ + +template <typename IMPL_RTL_STRINGDATA> void acquire(IMPL_RTL_STRINGDATA * pThis) +{ + if (!SAL_STRING_IS_STATIC (pThis)) + osl_atomic_increment( &((pThis)->refCount) ); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRINGDATA> void release( IMPL_RTL_STRINGDATA* pThis ) +{ + if (SAL_UNLIKELY(SAL_STRING_IS_STATIC (pThis))) + return; + + /* OString doesn't have an 'intern' */ + if constexpr (sizeof(STRCODE<IMPL_RTL_STRINGDATA>) == sizeof(sal_Unicode)) + { + if (SAL_STRING_IS_INTERN (pThis)) + { + internRelease (pThis); + return; + } + } + + if ( !osl_atomic_decrement( &(pThis->refCount) ) ) + { + RTL_LOG_STRING_DELETE( pThis ); + rtl_freeString( pThis ); + } +} + +/* ----------------------------------------------------------------------- */ + +/* static data to be referenced by all empty strings + * the refCount is predefined to 1 and must never become 0 ! + */ +template <typename IMPL_RTL_STRINGDATA> struct EmptyStringImpl +{ + static IMPL_RTL_STRINGDATA data; +}; + +template <> +inline rtl_uString EmptyStringImpl<rtl_uString>::data = { + sal_Int32(SAL_STRING_INTERN_FLAG | SAL_STRING_STATIC_FLAG | 1), /* sal_Int32 refCount; */ + 0, /* sal_Int32 length; */ + { 0 } /* sal_Unicode buffer[1]; */ +}; + +template <> +inline rtl_String EmptyStringImpl<rtl_String>::data = { + SAL_STRING_STATIC_FLAG | 1, /* sal_Int32 refCount; */ + 0, /* sal_Int32 length; */ + { 0 } /* char buffer[1]; */ +}; + +template <typename IMPL_RTL_STRINGDATA> void new_( IMPL_RTL_STRINGDATA** ppThis ) +{ + assert(ppThis); + if ( *ppThis) + release( *ppThis ); + + *ppThis = &EmptyStringImpl<IMPL_RTL_STRINGDATA>::data; +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRINGDATA> +void new_WithLength( IMPL_RTL_STRINGDATA** ppThis, sal_Int32 nLen ) +{ + assert(ppThis); + assert(nLen >= 0); + if ( nLen <= 0 ) + new_( ppThis ); + else + { + if ( *ppThis) + release( *ppThis ); + + *ppThis = Alloc<IMPL_RTL_STRINGDATA>( nLen ); + assert(*ppThis != nullptr); + (*ppThis)->length = 0; + (*ppThis)->buffer[0] = 0; + } +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRINGDATA, typename C> +void newFromStr_WithLength(IMPL_RTL_STRINGDATA** ppThis, const C* pCharStr, sal_Int32 nLen, + sal_Int32 allocExtra = 0) +{ + assert(ppThis); + assert(nLen >= 0); + assert(pCharStr || nLen == 0); + assert(allocExtra >= 0); + + if (nLen + allocExtra == 0) + return new_(ppThis); + + IMPL_RTL_STRINGDATA* pOrg = *ppThis; + *ppThis = Alloc<IMPL_RTL_STRINGDATA>(nLen + allocExtra); + assert(*ppThis != nullptr); + if (nLen > 0) + Copy((*ppThis)->buffer, pCharStr, nLen); + if (allocExtra > 0) + { + (*ppThis)->length = nLen; + (*ppThis)->buffer[nLen] = 0; + } + + RTL_LOG_STRING_NEW(*ppThis); + + /* must be done last, if pCharStr belongs to *ppThis */ + if (pOrg) + release(pOrg); +} + +template <typename IMPL_RTL_STRINGDATA> +void newFromString ( IMPL_RTL_STRINGDATA** ppThis, + const IMPL_RTL_STRINGDATA* pStr ) +{ + assert(pStr); + + newFromStr_WithLength(ppThis, pStr->buffer, pStr->length); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRINGDATA> +void newFromStr ( IMPL_RTL_STRINGDATA** ppThis, + const STRCODE<IMPL_RTL_STRINGDATA>* pCharStr ) +{ +#if OSL_DEBUG_LEVEL > 0 + //TODO: For now, only abort in non-production debug builds; once all places that rely on the + // undocumented newFromStr behavior of treating a null pCharStr like an empty string have been + // found and fixed, drop support for that behavior and turn this into a general assert: + if (pCharStr == nullptr) { + std::abort(); + } +#endif + + newFromStr_WithLength(ppThis, pCharStr, pCharStr ? getLength(pCharStr) : 0); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRINGDATA> void assign(IMPL_RTL_STRINGDATA**, IMPL_RTL_STRINGDATA*); + +template <typename IMPL_RTL_STRINGDATA> +void newFromSubString ( IMPL_RTL_STRINGDATA** ppThis, + const IMPL_RTL_STRINGDATA* pFrom, + sal_Int32 beginIndex, + sal_Int32 count ) +{ + assert(ppThis); + if ( beginIndex == 0 && count == pFrom->length ) + return assign(ppThis, const_cast<IMPL_RTL_STRINGDATA*>(pFrom)); + if ( count < 0 || beginIndex < 0 || beginIndex + count > pFrom->length ) + { + assert(false); // fail fast at least in debug builds + return newFromStr_WithLength(ppThis, "!!br0ken!!", 10); + } + + newFromStr_WithLength( ppThis, pFrom->buffer + beginIndex, count ); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRINGDATA> +void assign ( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr ) +{ + assert(ppThis); + /* must be done at first, if pStr == *ppThis */ + acquire( pStr ); + + if ( *ppThis ) + release( *ppThis ); + + *ppThis = pStr; +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRINGDATA> auto* getStr( IMPL_RTL_STRINGDATA* pThis ) +{ + assert(pThis); + return pThis->buffer; +} + +/* ----------------------------------------------------------------------- */ + +enum ThrowPolicy { NoThrow, Throw }; + +template <ThrowPolicy throwPolicy, typename IMPL_RTL_STRINGDATA, typename C1, typename C2> +void newConcat(IMPL_RTL_STRINGDATA** ppThis, const C1* pLeft, sal_Int32 nLeftLength, + const C2* pRight, sal_Int32 nRightLength) +{ + assert(ppThis); + assert(nLeftLength >= 0); + assert(pLeft || nLeftLength == 0); + assert(nRightLength >= 0); + assert(pRight || nRightLength == 0); + IMPL_RTL_STRINGDATA* pOrg = *ppThis; + + if (nLeftLength > std::numeric_limits<sal_Int32>::max() - nRightLength) + { + if constexpr (throwPolicy == NoThrow) + *ppThis = nullptr; + else + { +#if !defined(__COVERITY__) + throw std::length_error("newConcat"); +#else + //coverity doesn't report std::bad_alloc as an unhandled exception when + //potentially thrown from destructors but does report std::length_error + throw std::bad_alloc(); +#endif + } + } + else + { + auto* pTempStr = Alloc<IMPL_RTL_STRINGDATA>(nLeftLength + nRightLength); + OSL_ASSERT(pTempStr != nullptr); + *ppThis = pTempStr; + if (*ppThis != nullptr) { + if (nLeftLength) + Copy( pTempStr->buffer, pLeft, nLeftLength ); + if (nRightLength) + Copy( pTempStr->buffer+nLeftLength, pRight, nRightLength ); + + RTL_LOG_STRING_NEW( *ppThis ); + } + } + + /* must be done last, if left or right == *ppThis */ + if ( pOrg ) + release( pOrg ); +} + +template<typename IMPL_RTL_STRINGDATA, typename IMPL_RTL_STRCODE> +void newConcat(IMPL_RTL_STRINGDATA** ppThis, IMPL_RTL_STRINGDATA* pLeft, + const IMPL_RTL_STRCODE* pRight, sal_Int32 nRightLength) +{ + assert(pLeft != nullptr); + if (nRightLength == 0) + assign(ppThis, pLeft); + else + newConcat<Throw>(ppThis, pLeft->buffer, pLeft->length, pRight, nRightLength); +} + +template <typename IMPL_RTL_STRINGDATA> +void newConcat ( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pLeft, + IMPL_RTL_STRINGDATA* pRight ) +{ + /* Test for 0-Pointer - if not, change newReplaceStrAt! */ + if ( !pRight || !pRight->length ) + { + assert(pLeft != nullptr); + assign(ppThis, pLeft); + } + else if ( !pLeft || !pLeft->length ) + assign(ppThis, pRight); + else + newConcat<NoThrow>(ppThis, pLeft->buffer, pLeft->length, pRight->buffer, pRight->length); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRINGDATA> +void ensureCapacity ( IMPL_RTL_STRINGDATA** ppThis, + sal_Int32 size ) +{ + assert(ppThis); + IMPL_RTL_STRINGDATA* const pOrg = *ppThis; + if ( pOrg->refCount == 1 && pOrg->length >= size ) + return; + assert( pOrg->length <= size ); // do not truncate + auto* pTempStr = Alloc<IMPL_RTL_STRINGDATA>( size ); + Copy( pTempStr->buffer, pOrg->buffer, pOrg->length ); + // right now the length is still the same as of the original + pTempStr->length = pOrg->length; + pTempStr->buffer[ pOrg->length ] = '\0'; + *ppThis = pTempStr; + RTL_LOG_STRING_NEW( *ppThis ); + + release( pOrg ); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRINGDATA, typename IMPL_RTL_STRCODE> +void newReplaceStrAt ( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr, + sal_Int32 nIndex, + sal_Int32 nCount, + const IMPL_RTL_STRCODE* pNewSubStr, + sal_Int32 nNewSubStrLen ) +{ + assert(ppThis); + assert(nIndex >= 0 && nIndex <= pStr->length); + assert(nCount >= 0); + assert(nCount <= pStr->length - nIndex); + assert(pNewSubStr != nullptr || nNewSubStrLen == 0); + assert(nNewSubStrLen >= 0); + /* Append? */ + if ( nIndex >= pStr->length ) + return newConcat(ppThis, pStr, pNewSubStr, nNewSubStrLen); + + /* not more than the String length could be deleted */ + if ( nCount >= pStr->length-nIndex ) + { + /* Assign of NewSubStr? */ + if (nIndex == 0) + return newFromStr_WithLength( ppThis, pNewSubStr, nNewSubStrLen ); + + nCount = pStr->length - nIndex; + } + + /* Assign of Str? */ + if ( !nCount && !nNewSubStrLen ) + return assign(ppThis, pStr); + + IMPL_RTL_STRINGDATA* pOrg = *ppThis; + + /* Alloc New Buffer */ + *ppThis = Alloc<IMPL_RTL_STRINGDATA>(pStr->length - nCount + nNewSubStrLen); + assert(*ppThis != nullptr); + auto* pBuffer = (*ppThis)->buffer; + if ( nIndex ) + { + Copy( pBuffer, pStr->buffer, nIndex ); + pBuffer += nIndex; + } + if ( nNewSubStrLen ) + { + Copy( pBuffer, pNewSubStr, nNewSubStrLen ); + pBuffer += nNewSubStrLen; + } + Copy( pBuffer, pStr->buffer+nIndex+nCount, pStr->length-nIndex-nCount ); + + RTL_LOG_STRING_NEW( *ppThis ); + /* must be done last, if pStr or pNewSubStr == *ppThis */ + if ( pOrg ) + release( pOrg ); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRINGDATA> +void newReplaceStrAt ( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr, + sal_Int32 nIndex, + sal_Int32 nCount, + IMPL_RTL_STRINGDATA* pNewSubStr ) +{ + assert(ppThis); + assert(nIndex >= 0 && nIndex <= pStr->length); + assert(nCount >= 0); + assert(nCount <= pStr->length - nIndex); + /* Append? */ + if (nIndex >= pStr->length) + { + /* newConcat test, if pNewSubStr is 0 */ + newConcat(ppThis, pStr, pNewSubStr); + return; + } + + /* not more than the String length could be deleted */ + if (nCount >= pStr->length-nIndex) + { + /* Assign of NewSubStr? */ + if (nIndex == 0) + { + if (!pNewSubStr) + return new_(ppThis); + else + return assign(ppThis, pNewSubStr); + } + nCount = pStr->length - nIndex; + } + + /* Assign of Str? */ + if (!nCount && (!pNewSubStr || !pNewSubStr->length)) + { + assign(ppThis, pStr); + return; + } + + const auto* pNewSubStrBuf = pNewSubStr ? pNewSubStr->buffer : nullptr; + const sal_Int32 nNewSubStrLength = pNewSubStr ? pNewSubStr->length : 0; + newReplaceStrAt(ppThis, pStr, nIndex, nCount, pNewSubStrBuf, nNewSubStrLength); +} + +/* ----------------------------------------------------------------------- */ + +template <class Traits, typename IMPL_RTL_STRINGDATA> +void newReplaceChars(IMPL_RTL_STRINGDATA** ppThis, IMPL_RTL_STRINGDATA* pStr) +{ + assert(ppThis); + assert(pStr); + + const auto pEnd = pStr->buffer + pStr->length; + auto pCharStr = std::find_if(pStr->buffer, pEnd, [](auto c) { return Traits::Applicable(c); }); + if (pCharStr != pEnd) + { + IMPL_RTL_STRINGDATA* pOrg = *ppThis; + *ppThis = Alloc<IMPL_RTL_STRINGDATA>(pStr->length); + assert(*ppThis != nullptr); + auto* pNewCharStr = (*ppThis)->buffer; + /* Copy String */ + const sal_Int32 nCount = pCharStr - pStr->buffer; + Copy(pNewCharStr, pStr->buffer, nCount); + pNewCharStr += nCount; + /* replace/copy rest of the string */ + do + { + *pNewCharStr = Traits::Replace(*pCharStr); + pNewCharStr++; + pCharStr++; + } while (pCharStr != pEnd); + + RTL_LOG_STRING_NEW(*ppThis); + /* must be done last, if pStr == *ppThis */ + if (pOrg) + release(pOrg); + } + else + assign(ppThis, pStr); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRINGDATA> +void newTrim ( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr ) +{ + assert(pStr); + const auto view = trimView(pStr->buffer, pStr->length); + + if (static_cast<sal_Int32>(view.size()) == pStr->length) + assign(ppThis, pStr); + else + newFromStr_WithLength(ppThis, view.data(), view.size()); +} + +/* ----------------------------------------------------------------------- */ + +template <typename IMPL_RTL_STRINGDATA> +sal_Int32 getToken ( IMPL_RTL_STRINGDATA** ppThis, + IMPL_RTL_STRINGDATA* pStr, + sal_Int32 nToken, + STRCODE<IMPL_RTL_STRINGDATA> cTok, + sal_Int32 nIndex ) +{ + assert(ppThis); + assert(pStr); + assert(nIndex <= pStr->length); + + // Set ppThis to an empty string and return -1 if either nToken or nIndex is + // negative: + if (nIndex >= 0 && nToken >= 0) + { + const auto* pOrgCharStr = pStr->buffer; + const auto* pCharStr = pOrgCharStr + nIndex; + sal_Int32 nLen = pStr->length - nIndex; + sal_Int32 nTokCount = 0; + const auto* pCharStrStart = pCharStr; + while (nLen > 0) + { + if (*pCharStr == cTok) + { + nTokCount++; + + if (nTokCount > nToken) + break; + if (nTokCount == nToken) + pCharStrStart = pCharStr + 1; + } + + pCharStr++; + nLen--; + } + if (nTokCount >= nToken) + { + newFromStr_WithLength(ppThis, pCharStrStart, pCharStr - pCharStrStart); + if (nLen > 0) + return pCharStr - pOrgCharStr + 1; + else + return -1; + } + } + + new_(ppThis); + return -1; +} + +/* ======================================================================= */ +/* String buffer help functions */ +/* ======================================================================= */ + +template <class IMPL_RTL_STRINGDATA> +void stringbuffer_newFromStr_WithLength(IMPL_RTL_STRINGDATA** ppThis, + const STRCODE<IMPL_RTL_STRINGDATA>* pStr, sal_Int32 count) +{ + assert(ppThis); + assert(count >= 0); + if (!pStr) + count = 0; // Because old code didn't care about count when !pStr + + newFromStr_WithLength(ppThis, pStr, count, 16); +} + +template <class IMPL_RTL_STRINGDATA> +sal_Int32 stringbuffer_newFromStringBuffer(IMPL_RTL_STRINGDATA** ppThis, sal_Int32 capacity, + IMPL_RTL_STRINGDATA* pStr) +{ + assert(capacity >= 0); + assert(pStr); + + if (capacity < pStr->length) + capacity = pStr->length; + + newFromStr_WithLength(ppThis, pStr->buffer, pStr->length, capacity - pStr->length); + return capacity; +} + +template <class IMPL_RTL_STRINGDATA> +void stringbuffer_ensureCapacity(IMPL_RTL_STRINGDATA** ppThis, sal_Int32* capacity, + sal_Int32 minimumCapacity) +{ + assert(ppThis); + assert(capacity && *capacity >= 0); + // assert(minimumCapacity >= 0); // It was commented out in rtl_stringbuffer_ensureCapacity + if (minimumCapacity <= *capacity) + return; + + const auto nLength = (*ppThis)->length; + *capacity = (nLength + 1) * 2; + if (minimumCapacity > *capacity) + *capacity = minimumCapacity; + + newFromStr_WithLength(ppThis, (*ppThis)->buffer, nLength, *capacity - nLength); +} + +template <class IMPL_RTL_STRINGDATA, typename C> +void stringbuffer_insert(IMPL_RTL_STRINGDATA** ppThis, sal_Int32* capacity, sal_Int32 offset, + const C* pStr, sal_Int32 len) +{ + assert(ppThis); + assert(capacity && *capacity >= 0); + assert(offset >= 0 && offset <= (*ppThis)->length); + assert(len >= 0); + if (len == 0) + return; + + stringbuffer_ensureCapacity(ppThis, capacity, (*ppThis)->length + len); + + sal_Int32 nOldLen = (*ppThis)->length; + auto* pBuf = (*ppThis)->buffer; + + /* copy the tail */ + const sal_Int32 n = nOldLen - offset; + if (n > 0) + CopyBackward(pBuf + offset + len, pBuf + offset, n); + + /* insert the new characters */ + if (pStr != nullptr) + Copy(pBuf + offset, pStr, len); + + (*ppThis)->length = nOldLen + len; + pBuf[nOldLen + len] = 0; +} + +template <class IMPL_RTL_STRINGDATA> +void stringbuffer_remove(IMPL_RTL_STRINGDATA** ppThis, sal_Int32 start, sal_Int32 len) +{ + assert(ppThis); + assert(start >= 0 && start <= (*ppThis)->length); + assert(len >= 0); + + if (len > (*ppThis)->length - start) + len = (*ppThis)->length - start; + + //remove nothing + if (!len) + return; + + auto* pBuf = (*ppThis)->buffer; + const sal_Int32 nTailLen = (*ppThis)->length - (start + len); + + if (nTailLen) + { + /* move the tail */ + Copy(pBuf + start, pBuf + start + len, nTailLen); + } + + (*ppThis)->length -= len; + pBuf[(*ppThis)->length] = 0; +} + +template <class S, typename CharTypeFrom, typename CharTypeTo> +void newReplaceAllFromIndex(S** s, S* s1, CharTypeFrom const* from, sal_Int32 fromLength, + CharTypeTo const* to, sal_Int32 toLength, sal_Int32 fromIndex) +{ + assert(s != nullptr); + assert(s1 != nullptr); + assert(fromLength >= 0); + assert(from != nullptr || fromLength == 0); + assert(toLength >= 0); + assert(to != nullptr || toLength == 0); + assert(fromIndex >= 0 && fromIndex <= s1->length); + sal_Int32 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex, + from, fromLength); + if (i >= 0) + { + if (s1->length - fromLength > SAL_MAX_INT32 - toLength) + std::abort(); + i += fromIndex; + sal_Int32 nCapacity = s1->length + (toLength - fromLength); + if (fromLength < toLength) + { + // Pre-allocate up to 16 replacements more + const sal_Int32 nMaxMoreFinds = (s1->length - i - fromLength) / fromLength; + const sal_Int32 nIncrease = toLength - fromLength; + const sal_Int32 nMoreReplacements = std::min( + { nMaxMoreFinds, (SAL_MAX_INT32 - nCapacity) / nIncrease, sal_Int32(16) }); + nCapacity += nMoreReplacements * nIncrease; + } + const auto pOld = *s; + *s = Alloc<S>(nCapacity); + (*s)->length = 0; + fromIndex = 0; + do + { + stringbuffer_insert(s, &nCapacity, (*s)->length, s1->buffer + fromIndex, i); + stringbuffer_insert(s, &nCapacity, (*s)->length, to, toLength); + fromIndex += i + fromLength; + i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex, + from, fromLength); + } while (i >= 0); + // the rest + stringbuffer_insert(s, &nCapacity, (*s)->length, + s1->buffer + fromIndex, s1->length - fromIndex); + if (pOld) + release(pOld); // Must be last in case *s == s1 + } + else + assign(s, s1); + + RTL_LOG_STRING_NEW(*s); +} + +template <typename IMPL_RTL_STRINGDATA> +void newReplace(IMPL_RTL_STRINGDATA** ppThis, IMPL_RTL_STRINGDATA* pStr, + STRCODE<IMPL_RTL_STRINGDATA> cOld, STRCODE<IMPL_RTL_STRINGDATA> cNew) +{ + return newReplaceAllFromIndex(ppThis, pStr, &cOld, 1, &cNew, 1, 0); +} + +template <class S, typename CharTypeFrom, typename CharTypeTo> +void newReplaceFirst(S** s, S* s1, CharTypeFrom const* from, sal_Int32 fromLength, + CharTypeTo const* to, sal_Int32 toLength, sal_Int32& fromIndex) +{ + assert(s != nullptr); + assert(s1 != nullptr); + assert(fromLength >= 0); + assert(from != nullptr || fromLength == 0); + assert(toLength >= 0); + assert(to != nullptr || toLength == 0); + assert(fromIndex >= 0 && fromIndex <= s1->length); + sal_Int32 i = indexOfStr_WithLength(s1->buffer + fromIndex, s1->length - fromIndex, + from, fromLength); + if (i >= 0) + { + if (s1->length - fromLength > SAL_MAX_INT32 - toLength) + std::abort(); + i += fromIndex; + newReplaceStrAt(s, s1, i, fromLength, to, toLength); + } + else + assign(s, s1); + + fromIndex = i; +} + +// doubleToString implementation + +static inline constexpr sal_uInt64 eX[] = { 10ull, + 100ull, + 1000ull, + 10000ull, + 100000ull, + 1000000ull, + 10000000ull, + 100000000ull, + 1000000000ull, + 10000000000ull, + 100000000000ull, + 1000000000000ull, + 10000000000000ull, + 100000000000000ull, + 1000000000000000ull, + 10000000000000000ull, + 100000000000000000ull, + 1000000000000000000ull, + 10000000000000000000ull }; + +template <typename S> +void doubleToString(S** pResult, sal_Int32* pResultCapacity, sal_Int32 nResultOffset, double fValue, + rtl_math_StringFormat eFormat, sal_Int32 nDecPlaces, STRCODE<S> cDecSeparator, + sal_Int32 const* pGroups, STRCODE<S> cGroupSeparator, + bool bEraseTrailingDecZeros) +{ + auto decimalDigits = [](sal_uInt64 n) { + return std::distance(std::begin(eX), std::upper_bound(std::begin(eX), std::end(eX), n)) + 1; + }; + + auto roundToPow10 = [](sal_uInt64 n, int e) { + assert(e > 0 && o3tl::make_unsigned(e) <= std::size(eX)); + const sal_uInt64 d = eX[e - 1]; + return (n + d / 2) / d * d; + }; + + auto append = [](S** s, sal_Int32* pCapacity, sal_Int32& rOffset, auto sv) + { + stringbuffer_insert(s, pCapacity, rOffset, sv.data(), sv.size()); + rOffset += sv.size(); + }; + + if (std::isnan(fValue)) + { + // #i112652# XMLSchema-2 + constexpr std::string_view nan{ "NaN" }; + if (!pResultCapacity) + return newFromStr_WithLength(pResult, nan.data(), nan.size()); + else + return append(pResult, pResultCapacity, nResultOffset, nan); + } + + // sign adjustment, instead of testing for fValue<0.0 this will also fetch -0.0 + bool bSign = std::signbit(fValue); + + if (std::isinf(fValue)) + { + // #i112652# XMLSchema-2 + std::string_view inf = bSign ? std::string_view("-INF") : std::string_view("INF"); + if (!pResultCapacity) + return newFromStr_WithLength(pResult, inf.data(), inf.size()); + else + return append(pResult, pResultCapacity, nResultOffset, inf); + } + + if (bSign) + fValue = -fValue; + + decltype(jkj::dragonbox::to_decimal(fValue, jkj::dragonbox::policy::sign::ignore, + jkj::dragonbox::policy::trailing_zero::ignore)) aParts{}; + if (fValue) // to_decimal is documented to only handle non-zero finite numbers + aParts = jkj::dragonbox::to_decimal(fValue, jkj::dragonbox::policy::sign::ignore, + jkj::dragonbox::policy::trailing_zero::ignore); + + int nOrigDigits = decimalDigits(aParts.significand); + int nExp = nOrigDigits + aParts.exponent - 1; + int nRoundDigits = 15; + + // Unfortunately the old rounding below writes 1.79769313486232e+308 for + // DBL_MAX and 4 subsequent nextafter(...,0). + static const double fB1 = std::nextafter(std::numeric_limits<double>::max(), 0); + static const double fB2 = std::nextafter(fB1, 0); + static const double fB3 = std::nextafter(fB2, 0); + static const double fB4 = std::nextafter(fB3, 0); + if ((fValue >= fB4) && eFormat != rtl_math_StringFormat_F) + { + // 1.7976931348623157e+308 instead of rounded 1.79769313486232e+308 + // that can't be converted back as out of range. For rounded values if + // they exceed range they should not be written to exchange strings or + // file formats. + + eFormat = rtl_math_StringFormat_E; + nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, 0, 16); + nRoundDigits = 17; + } + + // Use integer representation for integer values that fit into the + // mantissa (1.((2^53)-1)) with a precision of 1 for highest accuracy. + if ((eFormat == rtl_math_StringFormat_Automatic || eFormat == rtl_math_StringFormat_F) + && aParts.exponent >= 0 && fValue < 0x1p53) + { + eFormat = rtl_math_StringFormat_F; + if (nDecPlaces == rtl_math_DecimalPlaces_Max) + nDecPlaces = 0; + else + nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, -15, 15); + + if (bEraseTrailingDecZeros && nDecPlaces > 0) + nDecPlaces = 0; + + nRoundDigits = nOrigDigits; // no rounding + } + + switch (eFormat) + { + case rtl_math_StringFormat_Automatic: + // E or F depending on exponent magnitude + if (nExp <= -15 || nExp >= 15) + { + if (nDecPlaces == rtl_math_DecimalPlaces_Max) + nDecPlaces = 14; + eFormat = rtl_math_StringFormat_E; + } + else + { + if (nDecPlaces == rtl_math_DecimalPlaces_Max) + nDecPlaces = (nExp < 14) ? 15 - nExp - 1 : 15; + eFormat = rtl_math_StringFormat_F; + } + break; + + case rtl_math_StringFormat_G: + case rtl_math_StringFormat_G1: + case rtl_math_StringFormat_G2: + // G-Point, similar to sprintf %G + if (nDecPlaces == rtl_math_DecimalPlaces_DefaultSignificance) + nDecPlaces = 6; + + if (nExp < -4 || nExp >= nDecPlaces) + { + nDecPlaces = std::max<sal_Int32>(1, nDecPlaces - 1); + + if (eFormat == rtl_math_StringFormat_G) + eFormat = rtl_math_StringFormat_E; + else if (eFormat == rtl_math_StringFormat_G2) + eFormat = rtl_math_StringFormat_E2; + else if (eFormat == rtl_math_StringFormat_G1) + eFormat = rtl_math_StringFormat_E1; + } + else + { + nDecPlaces = std::max<sal_Int32>(0, nDecPlaces - nExp - 1); + eFormat = rtl_math_StringFormat_F; + } + break; + + default: + break; + } + + // Too large values for nDecPlaces make no sense; it might also be + // rtl_math_DecimalPlaces_Max was passed with rtl_math_StringFormat_F or + // others, but we don't want to allocate/deallocate 2GB just to fill it + // with trailing '0' characters.. + nDecPlaces = std::clamp<sal_Int32>(nDecPlaces, -20, 20); + + sal_Int32 nDigits = nDecPlaces + 1; + + if (eFormat == rtl_math_StringFormat_F) + nDigits += nExp; + + // Round the number + nRoundDigits = std::min<int>(nDigits, nRoundDigits); + if (nDigits >= 0 && nOrigDigits > nRoundDigits) + { + aParts.significand = roundToPow10(aParts.significand, nOrigDigits - nRoundDigits); + assert(aParts.significand <= eX[nOrigDigits - 1]); + if (aParts.significand == eX[nOrigDigits - 1]) // up-rounding to the next decade + { + nOrigDigits++; + nExp++; + + if (eFormat == rtl_math_StringFormat_F) + nDigits++; + } + } + + sal_Int32 nBuf + = (nDigits <= 0 ? std::max<sal_Int32>(nDecPlaces, std::abs(nExp)) : nDigits + nDecPlaces) + + 10 + (pGroups ? std::abs(nDigits) * 2 : 0); + // max(nDigits) = max(nDecPlaces) + 1 + max(nExp) + 1 = 20 + 1 + 308 + 1 = 330 + // max(nBuf) = max(nDigits) + max(nDecPlaces) + 10 + max(nDigits) * 2 = 330 * 3 + 20 + 10 = 1020 + assert(nBuf <= 1024); + STRCODE<S>* const pBuf = static_cast<STRCODE<S>*>(alloca(nBuf * sizeof(STRCODE<S>))); + STRCODE<S>* p = pBuf; + if (bSign) + *p++ = '-'; + + bool bHasDec = false; + + int nDecPos; + // Check for F format and number < 1 + if (eFormat == rtl_math_StringFormat_F) + { + if (nExp < 0) + { + *p++ = '0'; + if (nDecPlaces > 0) + { + *p++ = cDecSeparator; + bHasDec = true; + } + + sal_Int32 i = (nDigits <= 0 ? nDecPlaces : -nExp - 1); + + while ((i--) > 0) + *p++ = '0'; + + nDecPos = 0; + } + else + nDecPos = nExp + 1; + } + else + nDecPos = 1; + + int nGrouping = 0, nGroupSelector = 0, nGroupExceed = 0; + if (nDecPos > 1 && pGroups && pGroups[0] && cGroupSeparator) + { + while (nGrouping + pGroups[nGroupSelector] < nDecPos) + { + nGrouping += pGroups[nGroupSelector]; + if (pGroups[nGroupSelector + 1]) + { + if (nGrouping + pGroups[nGroupSelector + 1] >= nDecPos) + break; // while + + ++nGroupSelector; + } + else if (!nGroupExceed) + nGroupExceed = nGrouping; + } + } + + // print the number + if (nDigits > 0) + { + for (int nCurExp = nOrigDigits - 1;;) + { + int nDigit; + if (aParts.significand > 0 && nCurExp > 0) + { + --nCurExp; + nDigit = aParts.significand / eX[nCurExp]; + aParts.significand %= eX[nCurExp]; + } + else + { + nDigit = aParts.significand; + aParts.significand = 0; + } + assert(nDigit >= 0 && nDigit < 10); + *p++ = nDigit + '0'; + + if (!--nDigits) + break; // for + + if (nDecPos) + { + if (!--nDecPos) + { + *p++ = cDecSeparator; + bHasDec = true; + } + else if (nDecPos == nGrouping) + { + *p++ = cGroupSeparator; + nGrouping -= pGroups[nGroupSelector]; + + if (nGroupSelector && nGrouping < nGroupExceed) + --nGroupSelector; + } + } + } + } + + if (!bHasDec && eFormat == rtl_math_StringFormat_F) + { // nDecPlaces < 0 did round the value + while (--nDecPos > 0) + { // fill before decimal point + if (nDecPos == nGrouping) + { + *p++ = cGroupSeparator; + nGrouping -= pGroups[nGroupSelector]; + + if (nGroupSelector && nGrouping < nGroupExceed) + --nGroupSelector; + } + + *p++ = '0'; + } + } + + if (bEraseTrailingDecZeros && bHasDec) + { + while (*(p - 1) == '0') + p--; + + if (*(p - 1) == cDecSeparator) + p--; + } + + // Print the exponent ('E', followed by '+' or '-', followed by exactly + // three digits for rtl_math_StringFormat_E). The code in + // rtl_[u]str_valueOf{Float|Double} relies on this format. + if (eFormat == rtl_math_StringFormat_E || eFormat == rtl_math_StringFormat_E2 + || eFormat == rtl_math_StringFormat_E1) + { + if (p == pBuf) + *p++ = '1'; + // maybe no nDigits if nDecPlaces < 0 + + *p++ = 'E'; + if (nExp < 0) + { + nExp = -nExp; + *p++ = '-'; + } + else + *p++ = '+'; + + if (eFormat == rtl_math_StringFormat_E || nExp >= 100) + *p++ = nExp / 100 + '0'; + + nExp %= 100; + + if (eFormat == rtl_math_StringFormat_E || eFormat == rtl_math_StringFormat_E2 || nExp >= 10) + *p++ = nExp / 10 + '0'; + + *p++ = nExp % 10 + '0'; + } + + if (!pResultCapacity) + newFromStr_WithLength(pResult, pBuf, p - pBuf); + else + append(pResult, pResultCapacity, nResultOffset, std::basic_string_view(pBuf, p - pBuf)); +} + +template <sal_Int32 maxLen, typename C, typename T> sal_Int32 SAL_CALL valueOfFP(C* pStr, T f) +{ + assert(pStr); + STRINGDATA<C>* pResult = nullptr; + doubleToString(&pResult, nullptr, 0, f, rtl_math_StringFormat_G, + maxLen - std::size("-x.E-xxx") + 1, '.', nullptr, 0, true); + const sal_Int32 nLen = pResult->length; + assert(nLen < maxLen); + Copy(pStr, pResult->buffer, nLen + 1); + release(pResult); + return nLen; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/unload.cxx b/sal/rtl/unload.cxx new file mode 100644 index 000000000..beac56ffa --- /dev/null +++ b/sal/rtl/unload.cxx @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <osl/time.h> +#include <rtl/unload.h> + +extern "C" void rtl_moduleCount_acquire(rtl_ModuleCount*) {} + +extern "C" void rtl_moduleCount_release(rtl_ModuleCount*) {} + +extern "C" sal_Bool rtl_moduleCount_canUnload(rtl_StandardModuleCount*, TimeValue*) +{ + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/uri.cxx b/sal/rtl/uri.cxx new file mode 100644 index 000000000..341455261 --- /dev/null +++ b/sal/rtl/uri.cxx @@ -0,0 +1,759 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <rtl/character.hxx> +#include <rtl/strbuf.hxx> +#include <rtl/textenc.h> +#include <rtl/textcvt.h> +#include <rtl/uri.h> +#include <rtl/uri.hxx> +#include <rtl/ustrbuf.h> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.h> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <sal/macros.h> + +#include <uri_internal.hxx> + +#include <algorithm> +#include <cstddef> + +namespace { + +sal_Unicode const cEscapePrefix = 0x25; // '%' + +int getHexWeight(sal_uInt32 nUtf32) +{ + return nUtf32 >= 0x30 && nUtf32 <= 0x39 ? // '0'--'9' + static_cast< int >(nUtf32 - 0x30) : + nUtf32 >= 0x41 && nUtf32 <= 0x46 ? // 'A'--'F' + static_cast< int >(nUtf32 - 0x41 + 10) : + nUtf32 >= 0x61 && nUtf32 <= 0x66 ? // 'a'--'f' + static_cast< int >(nUtf32 - 0x61 + 10) : + -1; // not a hex digit +} + +bool isValid(sal_Bool const * pCharClass, sal_uInt32 nUtf32) +{ + return nUtf32 < rtl::UriCharClassSize && pCharClass[nUtf32]; +} + +void writeUnicode(rtl_uString ** pBuffer, sal_Int32 * pCapacity, + sal_Unicode cChar) +{ + rtl_uStringbuffer_insert(pBuffer, pCapacity, (*pBuffer)->length, &cChar, 1); +} + +} + +namespace rtl::uri::detail { + +/** Read any of the following: + + @li sequence of escape sequences representing character from eCharset, + translated to single UCS4 character; or + @li pair of UTF-16 surrogates, translated to single UCS4 character; or + @li single UTF-16 character, extended to UCS4 character. + */ +sal_uInt32 readUcs4(sal_Unicode const ** pBegin, sal_Unicode const * pEnd, + bool bEncoded, rtl_TextEncoding eCharset, + EscapeType * pType) +{ + sal_uInt32 nChar = *(*pBegin)++; + int nWeight1; + int nWeight2; + if (nChar == cEscapePrefix && bEncoded && pEnd - *pBegin >= 2 + && (nWeight1 = getHexWeight((*pBegin)[0])) >= 0 + && (nWeight2 = getHexWeight((*pBegin)[1])) >= 0) + { + *pBegin += 2; + nChar = static_cast< sal_uInt32 >(nWeight1 << 4 | nWeight2); + if (nChar <= 0x7F) + { + *pType = EscapeChar; + } + else if (eCharset == RTL_TEXTENCODING_UTF8) + { + if (nChar >= 0xC0 && nChar <= 0xF4) + { + sal_uInt32 nEncoded; + int nShift; + sal_uInt32 nMin; + if (nChar <= 0xDF) + { + nEncoded = (nChar & 0x1F) << 6; + nShift = 0; + nMin = 0x80; + } + else if (nChar <= 0xEF) + { + nEncoded = (nChar & 0x0F) << 12; + nShift = 6; + nMin = 0x800; + } + else + { + nEncoded = (nChar & 0x07) << 18; + nShift = 12; + nMin = 0x10000; + } + + sal_Unicode const * p = *pBegin; + bool bUTF8 = true; + + for (; nShift >= 0; nShift -= 6) + { + if (pEnd - p < 3 || p[0] != cEscapePrefix + || (nWeight1 = getHexWeight(p[1])) < 8 + || nWeight1 > 11 + || (nWeight2 = getHexWeight(p[2])) < 0) + { + bUTF8 = false; + break; + } + p += 3; + nEncoded |= ((nWeight1 & 3) << 4 | nWeight2) << nShift; + } + if (bUTF8 && rtl::isUnicodeScalarValue(nEncoded) + && nEncoded >= nMin) + { + *pBegin = p; + *pType = EscapeChar; + return nEncoded; + } + } + *pType = EscapeOctet; + } + else + { + OStringBuffer aBuf; + aBuf.append(static_cast< char >(nChar)); + rtl_TextToUnicodeConverter aConverter + = rtl_createTextToUnicodeConverter(eCharset); + sal_Unicode const * p = *pBegin; + + for (;;) + { + sal_Unicode aDst[2]; + sal_uInt32 nInfo; + sal_Size nConverted; + sal_Size nDstSize = rtl_convertTextToUnicode( + aConverter, nullptr, aBuf.getStr(), aBuf.getLength(), aDst, + SAL_N_ELEMENTS( aDst ), + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR + | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR + | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR), + &nInfo, &nConverted); + + if (nInfo == 0) + { + assert( nConverted + == sal::static_int_cast< sal_uInt32 >( + aBuf.getLength())); + + rtl_destroyTextToUnicodeConverter(aConverter); + *pBegin = p; + *pType = EscapeChar; + + assert( nDstSize == 1 + || (nDstSize == 2 && rtl::isHighSurrogate(aDst[0]) + && rtl::isLowSurrogate(aDst[1]))); + + return nDstSize == 1 + ? aDst[0] : rtl::combineSurrogates(aDst[0], aDst[1]); + } + if (nInfo == RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOOSMALL + && pEnd - p >= 3 && p[0] == cEscapePrefix + && (nWeight1 = getHexWeight(p[1])) >= 0 + && (nWeight2 = getHexWeight(p[2])) >= 0) + { + p += 3; + aBuf.append(static_cast< char >(nWeight1 << 4 | nWeight2)); + } + else if (nInfo == RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOOSMALL + && p != pEnd && *p <= 0x7F) + { + aBuf.append(static_cast< char >(*p++)); + } + else + { + assert( + (nInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL) + == 0); + break; + } + } + rtl_destroyTextToUnicodeConverter(aConverter); + *pType = EscapeOctet; + } + return nChar; + } + + *pType = EscapeNo; + return rtl::isHighSurrogate(nChar) && *pBegin < pEnd + && rtl::isLowSurrogate(**pBegin) ? + rtl::combineSurrogates(nChar, *(*pBegin)++) : nChar; +} + +} + +namespace { + +void writeUcs4(rtl_uString ** pBuffer, sal_Int32 * pCapacity, sal_uInt32 nUtf32) +{ + rtl_uStringbuffer_insertUtf32(pBuffer, pCapacity, (*pBuffer)->length, nUtf32); +} + +void writeEscapeOctet(rtl_uString ** pBuffer, sal_Int32 * pCapacity, + sal_uInt32 nOctet) +{ + assert(nOctet <= 0xFF); // bad octet + + static sal_Unicode const aHex[16] + = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 }; /* '0'--'9', 'A'--'F' */ + + writeUnicode(pBuffer, pCapacity, cEscapePrefix); + writeUnicode(pBuffer, pCapacity, aHex[nOctet >> 4]); + writeUnicode(pBuffer, pCapacity, aHex[nOctet & 15]); +} + +bool writeEscapeChar(rtl_uString ** pBuffer, sal_Int32 * pCapacity, + sal_uInt32 nUtf32, rtl_TextEncoding eCharset, bool bStrict) +{ + assert(rtl::isUnicodeCodePoint(nUtf32)); + if (eCharset == RTL_TEXTENCODING_UTF8) + { + if (nUtf32 < 0x80) + { + writeEscapeOctet(pBuffer, pCapacity, nUtf32); + } + else if (nUtf32 < 0x800) + { + writeEscapeOctet(pBuffer, pCapacity, nUtf32 >> 6 | 0xC0); + writeEscapeOctet(pBuffer, pCapacity, (nUtf32 & 0x3F) | 0x80); + } + else if (nUtf32 < 0x10000) + { + writeEscapeOctet(pBuffer, pCapacity, nUtf32 >> 12 | 0xE0); + writeEscapeOctet(pBuffer, pCapacity, (nUtf32 >> 6 & 0x3F) | 0x80); + writeEscapeOctet(pBuffer, pCapacity, (nUtf32 & 0x3F) | 0x80); + } + else + { + writeEscapeOctet(pBuffer, pCapacity, nUtf32 >> 18 | 0xF0); + writeEscapeOctet(pBuffer, pCapacity, (nUtf32 >> 12 & 0x3F) | 0x80); + writeEscapeOctet(pBuffer, pCapacity, (nUtf32 >> 6 & 0x3F) | 0x80); + writeEscapeOctet(pBuffer, pCapacity, (nUtf32 & 0x3F) | 0x80); + } + } + else + { + rtl_UnicodeToTextConverter aConverter + = rtl_createUnicodeToTextConverter(eCharset); + sal_Unicode aSrc[2]; + sal_Size nSrcSize = rtl::splitSurrogates(nUtf32, aSrc); + + char aDst[32]; // FIXME random value + sal_uInt32 nInfo; + sal_Size nConverted; + sal_Size nDstSize = rtl_convertUnicodeToText( + aConverter, nullptr, aSrc, nSrcSize, aDst, sizeof aDst, + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR + | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR + | RTL_UNICODETOTEXT_FLAGS_FLUSH, + &nInfo, &nConverted); + assert((nInfo & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) == 0); + rtl_destroyUnicodeToTextConverter(aConverter); + + if (nInfo == 0) + { + assert(nConverted == nSrcSize); // bad rtl_convertUnicodeToText + + for (sal_Size i = 0; i < nDstSize; ++i) + { + writeEscapeOctet(pBuffer, pCapacity, + static_cast< unsigned char >(aDst[i])); + // FIXME all octets are escaped, even if there is no need + } + } + else + { + if (bStrict) + return false; + + writeUcs4(pBuffer, pCapacity, nUtf32); + } + } + return true; +} + +struct Component +{ + sal_Unicode const * pBegin; + sal_Unicode const * pEnd; + + Component(): pBegin(nullptr), pEnd(nullptr) {} + + bool isPresent() const { return pBegin != nullptr; } + + sal_Int32 getLength() const; +}; + +sal_Int32 Component::getLength() const +{ + assert(isPresent()); // taking length of non-present component + return static_cast< sal_Int32 >(pEnd - pBegin); +} + +struct Components +{ + Component aScheme; + Component aAuthority; + Component aPath; + Component aQuery; + Component aFragment; +}; + +void parseUriRef(rtl_uString const * pUriRef, Components * pComponents) +{ + // This algorithm is liberal and accepts various forms of illegal input. + + sal_Unicode const * pBegin = pUriRef->buffer; + sal_Unicode const * pEnd = pBegin + pUriRef->length; + sal_Unicode const * pPos = pBegin; + + if (pPos != pEnd && rtl::isAsciiAlpha(*pPos)) + { + for (sal_Unicode const * p = pPos + 1; p != pEnd; ++p) + { + if (*p == ':') + { + pComponents->aScheme.pBegin = pBegin; + pComponents->aScheme.pEnd = ++p; + pPos = p; + break; + } + + if (!rtl::isAsciiAlphanumeric(*p) && *p != '+' && *p != '-' + && *p != '.') + { + break; + } + } + } + + if (pEnd - pPos >= 2 && pPos[0] == '/' && pPos[1] == '/') + { + pComponents->aAuthority.pBegin = pPos; + pPos += 2; + while (pPos != pEnd && *pPos != '/' && *pPos != '?' && *pPos != '#') + { + ++pPos; + } + + pComponents->aAuthority.pEnd = pPos; + } + + pComponents->aPath.pBegin = pPos; + while (pPos != pEnd && *pPos != '?' && * pPos != '#') + { + ++pPos; + } + + pComponents->aPath.pEnd = pPos; + + if (pPos != pEnd && *pPos == '?') + { + pComponents->aQuery.pBegin = pPos++; + while (pPos != pEnd && * pPos != '#') + { + ++pPos; + } + + pComponents->aQuery.pEnd = pPos; + } + + if (pPos != pEnd) + { + assert(*pPos == '#'); + pComponents->aFragment.pBegin = pPos; + pComponents->aFragment.pEnd = pEnd; + } +} + +void appendPath( + OUStringBuffer & buffer, sal_Int32 bufferStart, bool precedingSlash, + sal_Unicode const * pathBegin, sal_Unicode const * pathEnd) +{ + while (precedingSlash || pathBegin != pathEnd) + { + sal_Unicode const * p = pathBegin; + while (p != pathEnd && *p != '/') + { + ++p; + } + + std::size_t n = p - pathBegin; + if (n == 1 && pathBegin[0] == '.') + { + // input begins with "." -> remove from input (and done): + // i.e., !precedingSlash -> !precedingSlash + // input begins with "./" -> remove from input: + // i.e., !precedingSlash -> !precedingSlash + // input begins with "/." -> replace with "/" in input (and not yet + // done): + // i.e., precedingSlash -> precedingSlash + // input begins with "/./" -> replace with "/" in input: + // i.e., precedingSlash -> precedingSlash + } + else if (n == 2 && pathBegin[0] == '.' && pathBegin[1] == '.') + { + // input begins with ".." -> remove from input (and done): + // i.e., !precedingSlash -> !precedingSlash + // input begins with "../" -> remove from input + // i.e., !precedingSlash -> !precedingSlash + // input begins with "/.." -> replace with "/" in input, and shrink + // output (not yet done): + // i.e., precedingSlash -> precedingSlash + // input begins with "/../" -> replace with "/" in input, and shrink + // output: + // i.e., precedingSlash -> precedingSlash + if (precedingSlash) + { + buffer.truncate( + bufferStart + + std::max<sal_Int32>( + rtl_ustr_lastIndexOfChar_WithLength( + buffer.getStr() + bufferStart, + buffer.getLength() - bufferStart, '/'), + 0)); + } + } + else + { + if (precedingSlash) + buffer.append('/'); + + buffer.append(pathBegin, n); + precedingSlash = p != pathEnd; + } + pathBegin = p + (p == pathEnd ? 0 : 1); + } +} + +} + +sal_Bool const * SAL_CALL rtl_getUriCharClass(rtl_UriCharClass eCharClass) + SAL_THROW_EXTERN_C() +{ + static constexpr std::array<sal_Bool, rtl::UriCharClassSize> aCharClass[] = { + rtl::createUriCharClass(u8""), // None + rtl::createUriCharClass( + u8"!$&'()*+,-./:;=?@[]_~" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // Uric + rtl::createUriCharClass( + u8"!$&'()*+,-.:;=?@_~" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // UricNoSlash + rtl::createUriCharClass( + u8"!$&'()*+,-.;=@_~" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // RelSegment + rtl::createUriCharClass( + u8"!$&'()*+,-.:;=@_~" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // RegName + rtl::createUriCharClass( + u8"!$&'()*+,-.:;=_~" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // Userinfo + rtl::createUriCharClass( + u8"!$&'()*+,-.:=@_~" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), // Pchar + rtl::createUriCharClass( + u8"!$&'()*+-./:?@_~" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")}; // UnoParamValue + + assert( + (eCharClass >= 0 + && (sal::static_int_cast< std::size_t >(eCharClass) + < SAL_N_ELEMENTS(aCharClass)))); // bad eCharClass + return aCharClass[eCharClass].data(); +} + +void SAL_CALL rtl_uriEncode(rtl_uString * pText, sal_Bool const * pCharClass, + rtl_UriEncodeMechanism eMechanism, + rtl_TextEncoding eCharset, rtl_uString ** pResult) + SAL_THROW_EXTERN_C() +{ + assert(!pCharClass[0x25]); // make sure the percent sign is encoded... + + sal_Unicode const * p = pText->buffer; + sal_Unicode const * pEnd = p + pText->length; + sal_Int32 nCapacity = 256; + rtl_uString_new_WithLength(pResult, nCapacity); + + while (p < pEnd) + { + rtl::uri::detail::EscapeType eType; + sal_uInt32 nUtf32 = rtl::uri::detail::readUcs4( + &p, pEnd, + (eMechanism == rtl_UriEncodeKeepEscapes + || eMechanism == rtl_UriEncodeCheckEscapes + || eMechanism == rtl_UriEncodeStrictKeepEscapes), + eCharset, &eType); + + switch (eType) + { + case rtl::uri::detail::EscapeNo: + if (isValid(pCharClass, nUtf32)) // implies nUtf32 <= 0x7F + { + writeUnicode(pResult, &nCapacity, + static_cast< sal_Unicode >(nUtf32)); + } + else if (!writeEscapeChar( + pResult, &nCapacity, nUtf32, eCharset, + (eMechanism == rtl_UriEncodeStrict + || eMechanism == rtl_UriEncodeStrictKeepEscapes))) + { + rtl_uString_new(pResult); + return; + } + break; + + case rtl::uri::detail::EscapeChar: + if (eMechanism == rtl_UriEncodeCheckEscapes + && isValid(pCharClass, nUtf32)) // implies nUtf32 <= 0x7F + { + writeUnicode(pResult, &nCapacity, + static_cast< sal_Unicode >(nUtf32)); + } + else if (!writeEscapeChar( + pResult, &nCapacity, nUtf32, eCharset, + (eMechanism == rtl_UriEncodeStrict + || eMechanism == rtl_UriEncodeStrictKeepEscapes))) + { + rtl_uString_new(pResult); + return; + } + break; + + case rtl::uri::detail::EscapeOctet: + writeEscapeOctet(pResult, &nCapacity, nUtf32); + break; + } + } + *pResult = rtl_uStringBuffer_makeStringAndClear(pResult, &nCapacity); +} + +void SAL_CALL rtl_uriDecode(rtl_uString * pText, + rtl_UriDecodeMechanism eMechanism, + rtl_TextEncoding eCharset, rtl_uString ** pResult) + SAL_THROW_EXTERN_C() +{ + switch (eMechanism) + { + case rtl_UriDecodeNone: + rtl_uString_assign(pResult, pText); + break; + + case rtl_UriDecodeToIuri: + eCharset = RTL_TEXTENCODING_UTF8; + [[fallthrough]]; + default: // rtl_UriDecodeWithCharset, rtl_UriDecodeStrict + { + sal_Unicode const * p = pText->buffer; + sal_Unicode const * pEnd = p + pText->length; + sal_Int32 nCapacity = pText->length; + rtl_uString_new_WithLength(pResult, nCapacity); + + while (p < pEnd) + { + rtl::uri::detail::EscapeType eType; + sal_uInt32 nUtf32 = rtl::uri::detail::readUcs4(&p, pEnd, true, eCharset, &eType); + switch (eType) + { + case rtl::uri::detail::EscapeChar: + if (nUtf32 <= 0x7F && eMechanism == rtl_UriDecodeToIuri) + { + writeEscapeOctet(pResult, &nCapacity, nUtf32); + break; + } + [[fallthrough]]; + + case rtl::uri::detail::EscapeNo: + writeUcs4(pResult, &nCapacity, nUtf32); + break; + + case rtl::uri::detail::EscapeOctet: + if (eMechanism == rtl_UriDecodeStrict) + { + rtl_uString_new(pResult); + return; + } + writeEscapeOctet(pResult, &nCapacity, nUtf32); + break; + } + } + + *pResult = rtl_uStringBuffer_makeStringAndClear( pResult, &nCapacity ); + } + break; + } +} + +sal_Bool SAL_CALL rtl_uriConvertRelToAbs(rtl_uString * pBaseUriRef, + rtl_uString * pRelUriRef, + rtl_uString ** pResult, + rtl_uString ** pException) + SAL_THROW_EXTERN_C() +{ + // Use the strict parser algorithm from RFC 3986, section 5.2, to turn the + // relative URI into an absolute one: + Components aRelComponents; + parseUriRef(pRelUriRef, &aRelComponents); + OUStringBuffer aBuffer(256); + + if (aRelComponents.aScheme.isPresent()) + { + aBuffer.append(aRelComponents.aScheme.pBegin, + aRelComponents.aScheme.getLength()); + + if (aRelComponents.aAuthority.isPresent()) + { + aBuffer.append(aRelComponents.aAuthority.pBegin, + aRelComponents.aAuthority.getLength()); + } + + appendPath( + aBuffer, aBuffer.getLength(), false, aRelComponents.aPath.pBegin, + aRelComponents.aPath.pEnd); + + if (aRelComponents.aQuery.isPresent()) + { + aBuffer.append(aRelComponents.aQuery.pBegin, + aRelComponents.aQuery.getLength()); + } + } + else + { + Components aBaseComponents; + parseUriRef(pBaseUriRef, &aBaseComponents); + if (!aBaseComponents.aScheme.isPresent()) + { + rtl_uString_assign( + pException, + (OUString( + "<" + OUString::unacquired(&pBaseUriRef) + + "> does not start with a scheme component") + .pData)); + return false; + } + + aBuffer.append(aBaseComponents.aScheme.pBegin, + aBaseComponents.aScheme.getLength()); + if (aRelComponents.aAuthority.isPresent()) + { + aBuffer.append(aRelComponents.aAuthority.pBegin, + aRelComponents.aAuthority.getLength()); + appendPath( + aBuffer, aBuffer.getLength(), false, + aRelComponents.aPath.pBegin, aRelComponents.aPath.pEnd); + + if (aRelComponents.aQuery.isPresent()) + { + aBuffer.append(aRelComponents.aQuery.pBegin, + aRelComponents.aQuery.getLength()); + } + } + else + { + if (aBaseComponents.aAuthority.isPresent()) + { + aBuffer.append(aBaseComponents.aAuthority.pBegin, + aBaseComponents.aAuthority.getLength()); + } + + if (aRelComponents.aPath.pBegin == aRelComponents.aPath.pEnd) + { + aBuffer.append(aBaseComponents.aPath.pBegin, + aBaseComponents.aPath.getLength()); + if (aRelComponents.aQuery.isPresent()) + { + aBuffer.append(aRelComponents.aQuery.pBegin, + aRelComponents.aQuery.getLength()); + } + else if (aBaseComponents.aQuery.isPresent()) + { + aBuffer.append(aBaseComponents.aQuery.pBegin, + aBaseComponents.aQuery.getLength()); + } + } + else + { + if (*aRelComponents.aPath.pBegin == '/') + { + appendPath( + aBuffer, aBuffer.getLength(), false, + aRelComponents.aPath.pBegin, aRelComponents.aPath.pEnd); + } + else if (aBaseComponents.aAuthority.isPresent() + && aBaseComponents.aPath.pBegin + == aBaseComponents.aPath.pEnd) + { + appendPath( + aBuffer, aBuffer.getLength(), true, + aRelComponents.aPath.pBegin, aRelComponents.aPath.pEnd); + } + else + { + sal_Int32 n = aBuffer.getLength(); + sal_Int32 i = rtl_ustr_lastIndexOfChar_WithLength( + aBaseComponents.aPath.pBegin, + aBaseComponents.aPath.getLength(), '/'); + + if (i >= 0) + { + appendPath( + aBuffer, n, false, aBaseComponents.aPath.pBegin, + aBaseComponents.aPath.pBegin + i); + } + + appendPath( + aBuffer, n, i >= 0, aRelComponents.aPath.pBegin, + aRelComponents.aPath.pEnd); + } + + if (aRelComponents.aQuery.isPresent()) + { + aBuffer.append(aRelComponents.aQuery.pBegin, + aRelComponents.aQuery.getLength()); + } + } + } + } + if (aRelComponents.aFragment.isPresent()) + { + aBuffer.append(aRelComponents.aFragment.pBegin, + aRelComponents.aFragment.getLength()); + } + + rtl_uString_assign(pResult, aBuffer.makeStringAndClear().pData); + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/ustrbuf.cxx b/sal/rtl/ustrbuf.cxx new file mode 100644 index 000000000..c91834cfa --- /dev/null +++ b/sal/rtl/ustrbuf.cxx @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <string.h> + +#include <osl/interlck.h> +#include <osl/diagnose.h> +#include <rtl/character.hxx> +#include <rtl/ustrbuf.hxx> +#include "strimp.hxx" + +#if USE_SDT_PROBES +#define RTL_LOG_STRING_BITS 16 +#endif + +#include "strtmpl.hxx" + +void SAL_CALL rtl_uStringbuffer_newFromStr_WithLength( rtl_uString ** newStr, + const sal_Unicode * value, + sal_Int32 count) +{ + rtl::str::stringbuffer_newFromStr_WithLength(newStr, value, count); +} + +rtl_uString * SAL_CALL rtl_uStringBuffer_refReturn( rtl_uString * pThis ) +{ + RTL_LOG_STRING_NEW( pThis ); + rtl::str::acquire( pThis ); + return pThis; +} + +rtl_uString * SAL_CALL rtl_uStringBuffer_makeStringAndClear( rtl_uString ** ppThis, + sal_Int32 *nCapacity ) +{ + assert(ppThis); + assert(nCapacity); + // avoid an un-necessary atomic ref/unref pair + rtl_uString *pStr = *ppThis; + *ppThis = nullptr; + + rtl::str::new_(ppThis); + *nCapacity = 0; + + RTL_LOG_STRING_NEW( pStr ); + + return pStr; +} + +sal_Int32 SAL_CALL rtl_uStringbuffer_newFromStringBuffer( rtl_uString ** newStr, + sal_Int32 capacity, + rtl_uString * oldStr ) +{ + return rtl::str::stringbuffer_newFromStringBuffer(newStr, capacity, oldStr); +} + +void SAL_CALL rtl_uStringbuffer_ensureCapacity + (rtl_uString ** This, sal_Int32* capacity, sal_Int32 minimumCapacity) +{ + rtl::str::stringbuffer_ensureCapacity(This, capacity, minimumCapacity); +} + +void SAL_CALL rtl_uStringbuffer_insert( rtl_uString ** This, + sal_Int32 * capacity, + sal_Int32 offset, + const sal_Unicode * str, + sal_Int32 len) +{ + rtl::str::stringbuffer_insert(This, capacity, offset, str, len); +} + +void rtl_uStringbuffer_insertUtf32( + rtl_uString ** pThis, sal_Int32 * capacity, sal_Int32 offset, sal_uInt32 c) + SAL_THROW_EXTERN_C() +{ + sal_Unicode buf[2]; + sal_Int32 len = rtl::splitSurrogates(c, buf); + rtl::str::stringbuffer_insert(pThis, capacity, offset, buf, len); +} + +void SAL_CALL rtl_uStringbuffer_insert_ascii( /*inout*/rtl_uString ** This, + /*inout*/sal_Int32 * capacity, + sal_Int32 offset, + const char * str, + sal_Int32 len) +{ + rtl::str::stringbuffer_insert(This, capacity, offset, str, len); +} + +/************************************************************************* + * rtl_uStringbuffer_remove + */ +void SAL_CALL rtl_uStringbuffer_remove( rtl_uString ** This, + sal_Int32 start, + sal_Int32 len ) +{ + rtl::str::stringbuffer_remove(This, start, len); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/ustring.cxx b/sal/rtl/ustring.cxx new file mode 100644 index 000000000..6fc0e8707 --- /dev/null +++ b/sal/rtl/ustring.cxx @@ -0,0 +1,1324 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> +#include <cstdlib> +#include <limits> +#include <stdexcept> +#include <string> + +#include <config_options.h> +#include <osl/diagnose.h> +#include <osl/interlck.h> +#include <osl/mutex.h> +#include <rtl/tencinfo.h> + +#include <string.h> +#include <sal/alloca.h> +#include <sal/log.hxx> + +#include "hash.hxx" +#include "strimp.hxx" +#include <rtl/character.hxx> +#include <rtl/ustring.h> + +#include <rtl/math.h> + +#if defined _WIN32 +// Temporary check to verify that the #pragma pack around rtl_uString is indeed cargo cult and can +// safely be removed: +static_assert(alignof (rtl_uString) == 4); +static_assert(sizeof (rtl_uString) == 12); +#endif + +/* ======================================================================= */ + +#if USE_SDT_PROBES +#define RTL_LOG_STRING_BITS 16 +#endif + +#include "strtmpl.hxx" + +/* ======================================================================= */ + +sal_Int32 rtl_ustr_indexOfAscii_WithLength( + sal_Unicode const * str, sal_Int32 len, + char const * subStr, sal_Int32 subLen) SAL_THROW_EXTERN_C() +{ + return rtl::str::indexOfStr_WithLength(str, len, subStr, subLen); +} + +sal_Int32 rtl_ustr_lastIndexOfAscii_WithLength( + sal_Unicode const * str, sal_Int32 len, + char const * subStr, sal_Int32 subLen) SAL_THROW_EXTERN_C() +{ + assert(len >= 0); + assert(subLen >= 0); + if (subLen > 0 && subLen <= len) { + sal_Int32 i; + for (i = len - subLen; i >= 0; --i) { + if (rtl_ustr_asciil_reverseEquals_WithLength( + str + i, subStr, subLen)) + { + return i; + } + } + } + return -1; +} + +sal_Int32 SAL_CALL rtl_ustr_valueOfFloat(sal_Unicode * pStr, float f) + SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfFP<RTL_USTR_MAX_VALUEOFFLOAT>(pStr, f); +} + +sal_Int32 SAL_CALL rtl_ustr_valueOfDouble(sal_Unicode * pStr, double d) + SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfFP<RTL_USTR_MAX_VALUEOFDOUBLE>(pStr, d); +} + +namespace { + +// Avoid -fsanitize=undefined warning e.g. "runtime error: value 1e+99 is +// outside the range of representable values of type 'float'" with Clang prior to +// <https://github.com/llvm/llvm-project/commit/9e52c43090f8cd980167bbd2719878ae36bcf6b5> "Treat the +// range of representable values of floating-point types as [-inf, +inf] not as [-max, +max]" +// (ENABLE_RUNTIME_OPTIMIZATIONS is an approximation for checking whether building is done without +// -fsanitize=undefined): +float doubleToFloat(double x) { +#if !defined __clang__ || __clang_major__ >= 9 || ENABLE_RUNTIME_OPTIMIZATIONS + return static_cast<float>(x); +#else + return + x < -std::numeric_limits<float>::max() + ? -std::numeric_limits<float>::infinity() + : x > std::numeric_limits<float>::max() + ? std::numeric_limits<float>::infinity() + : static_cast<float>(x); +#endif +} + +} + +float SAL_CALL rtl_ustr_toFloat(sal_Unicode const * pStr) SAL_THROW_EXTERN_C() +{ + assert(pStr); + return doubleToFloat(rtl_math_uStringToDouble(pStr, + pStr + rtl_ustr_getLength(pStr), + '.', 0, nullptr, nullptr)); +} + +double SAL_CALL rtl_ustr_toDouble(sal_Unicode const * pStr) SAL_THROW_EXTERN_C() +{ + assert(pStr); + return rtl_math_uStringToDouble(pStr, pStr + rtl_ustr_getLength(pStr), '.', + 0, nullptr, nullptr); +} + +/* ======================================================================= */ + +sal_Int32 SAL_CALL rtl_ustr_ascii_compare( const sal_Unicode* pStr1, + const char* pStr2 ) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::null_terminated(pStr1), rtl::str::null_terminated(pStr2), + rtl::str::CompareNormal(), rtl::str::noShortening); +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL rtl_ustr_ascii_compare_WithLength( const sal_Unicode* pStr1, + sal_Int32 nStr1Len, + const char* pStr2 ) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len), + rtl::str::null_terminated(pStr2), + rtl::str::CompareNormal(), rtl::str::noShortening); +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL rtl_ustr_ascii_shortenedCompare_WithLength( const sal_Unicode* pStr1, + sal_Int32 nStr1Len, + const char* pStr2, + sal_Int32 nShortenedLength ) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len), + rtl::str::null_terminated(pStr2), + rtl::str::CompareNormal(), nShortenedLength); +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL rtl_ustr_asciil_reverseCompare_WithLength( const sal_Unicode* pStr1, + sal_Int32 nStr1Len, + const char* pStr2, + sal_Int32 nStr2Len ) + SAL_THROW_EXTERN_C() +{ + return rtl::str::reverseCompare_WithLengths(pStr1, nStr1Len, pStr2, nStr2Len, + rtl::str::CompareNormal()); +} + +/* ----------------------------------------------------------------------- */ + +sal_Bool SAL_CALL rtl_ustr_asciil_reverseEquals_WithLength( const sal_Unicode* pStr1, + const char* pStr2, + sal_Int32 nStrLen ) + SAL_THROW_EXTERN_C() +{ + assert(nStrLen >= 0); + const sal_Unicode* pStr1Run = pStr1+nStrLen; + const char* pStr2Run = pStr2+nStrLen; + while ( pStr1 < pStr1Run ) + { + SAL_WARN_IF( !rtl::isAscii(static_cast<unsigned char>(*pStr2)), "rtl.string", + "rtl_ustr_asciil_reverseEquals_WithLength - Found char > 127" ); + pStr1Run--; + pStr2Run--; + if( *pStr1Run != static_cast<sal_Unicode>(*pStr2Run) ) + return false; + } + + return true; +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL rtl_ustr_ascii_compareIgnoreAsciiCase( const sal_Unicode* pStr1, + const char* pStr2 ) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::null_terminated(pStr1), rtl::str::null_terminated(pStr2), + rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening); +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( const sal_Unicode* pStr1, + sal_Int32 nStr1Len, + const char* pStr2 ) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len), + rtl::str::null_terminated(pStr2), + rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening); +} + +sal_Int32 rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( + sal_Unicode const * first, sal_Int32 firstLen, + char const * second, sal_Int32 secondLen) SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(first, firstLen), + rtl::str::with_length(second, secondLen), + rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening); +} + +/* ----------------------------------------------------------------------- */ + +sal_Int32 SAL_CALL rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( const sal_Unicode* pStr1, + sal_Int32 nStr1Len, + const char* pStr2, + sal_Int32 nShortenedLength ) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len), + rtl::str::null_terminated(pStr2), + rtl::str::CompareIgnoreAsciiCase(), nShortenedLength); +} + +/* ----------------------------------------------------------------------- */ + +void SAL_CALL rtl_uString_newFromAscii( rtl_uString** ppThis, + const char* pCharStr ) + SAL_THROW_EXTERN_C() +{ + assert(ppThis); + sal_Int32 nLen = pCharStr ? rtl::str::getLength(pCharStr) : 0; + + if ( !nLen ) + { + rtl_uString_new( ppThis ); + return; + } + + if ( *ppThis ) + rtl_uString_release( *ppThis ); + + *ppThis = rtl_uString_ImplAlloc( nLen ); + OSL_ASSERT(*ppThis != nullptr); + if ( !(*ppThis) ) + return; + + sal_Unicode* pBuffer = (*ppThis)->buffer; + do + { + assert(static_cast<unsigned char>(*pCharStr) < 0x80); // ASCII range + *pBuffer = *pCharStr; + pBuffer++; + pCharStr++; + } + while ( *pCharStr ); + + RTL_LOG_STRING_NEW( *ppThis ); +} + +void SAL_CALL rtl_uString_newFromCodePoints( + rtl_uString ** newString, sal_uInt32 const * codePoints, + sal_Int32 codePointCount) SAL_THROW_EXTERN_C() +{ + sal_Int32 n; + sal_Int32 i; + sal_Unicode * p; + assert(newString != nullptr); + assert((codePoints != nullptr || codePointCount == 0) && codePointCount >= 0); + if (codePointCount == 0) { + rtl_uString_new(newString); + return; + } + if (*newString != nullptr) { + rtl_uString_release(*newString); + } + n = codePointCount; + for (i = 0; i < codePointCount; ++i) { + OSL_ASSERT(rtl::isUnicodeCodePoint(codePoints[i])); + if (codePoints[i] >= 0x10000) { + ++n; + } + } + /* Builds on the assumption that sal_Int32 uses 32 bit two's complement + representation with wrap around (the necessary number of UTF-16 code + units will be no larger than 2 * SAL_MAX_INT32, represented as + sal_Int32 -2): */ + if (n < 0) { + // coverity[dead_error_begin] - assumes wrap around + *newString = nullptr; + return; + } + *newString = rtl_uString_ImplAlloc(n); + if (*newString == nullptr) { + return; + } + p = (*newString)->buffer; + for (i = 0; i < codePointCount; ++i) { + p += rtl::splitSurrogates(codePoints[i], p); + } + RTL_LOG_STRING_NEW( *newString ); +} + +void rtl_uString_newConcatAsciiL( + rtl_uString ** newString, rtl_uString * left, char const * right, + sal_Int32 rightLength) +{ + rtl::str::newConcat(newString, left, right, rightLength); +} + +void rtl_uString_newConcatUtf16L( + rtl_uString ** newString, rtl_uString * left, sal_Unicode const * right, + sal_Int32 rightLength) +{ + rtl::str::newConcat(newString, left, right, rightLength); +} + +/* ======================================================================= */ + +static int rtl_ImplGetFastUTF8UnicodeLen( const char* pStr, sal_Int32 nLen, bool * ascii ) +{ + int n; + const char* pEndStr; + + *ascii = true; + n = 0; + pEndStr = pStr+nLen; + while ( pStr < pEndStr ) + { + unsigned char c = static_cast<unsigned char>(*pStr); + + if ( !(c & 0x80) ) + pStr++; + else + { + if ( (c & 0xE0) == 0xC0 ) + pStr += 2; + else if ( (c & 0xF0) == 0xE0 ) + pStr += 3; + else if ( (c & 0xF8) == 0xF0 ) + pStr += 4; + else if ( (c & 0xFC) == 0xF8 ) + pStr += 5; + else if ( (c & 0xFE) == 0xFC ) + pStr += 6; + else + pStr++; + *ascii = false; + } + + n++; + } + + return n; +} + +/* ----------------------------------------------------------------------- */ + +static void rtl_string2UString_status( rtl_uString** ppThis, + const char* pStr, + sal_Int32 nLen, + rtl_TextEncoding eTextEncoding, + sal_uInt32 nCvtFlags, + sal_uInt32 *pInfo ) +{ + OSL_ENSURE(nLen == 0 || rtl_isOctetTextEncoding(eTextEncoding), + "rtl_string2UString_status() - Wrong TextEncoding" ); + + if ( !nLen ) + { + rtl_uString_new( ppThis ); + if (pInfo != nullptr) { + *pInfo = 0; + } + } + else + { + if ( *ppThis ) + rtl_uString_release( *ppThis ); + + /* Optimization for US-ASCII */ + if ( eTextEncoding == RTL_TEXTENCODING_ASCII_US ) + { + sal_Unicode* pBuffer; + *ppThis = rtl_uString_ImplAlloc( nLen ); + if (*ppThis == nullptr) { + if (pInfo != nullptr) { + *pInfo = RTL_TEXTTOUNICODE_INFO_ERROR | + RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL; + } + return; + } + pBuffer = (*ppThis)->buffer; + sal_Int32 nLenCopy(nLen); + const char *pStrCopy(pStr); + do + { + /* Check ASCII range */ + if (static_cast<unsigned char>(*pStrCopy) > 127) + { + rtl_uString_release(*ppThis); + goto retry; // cancel loop - try again with the converter + } + + *pBuffer = *pStrCopy; + pBuffer++; + pStrCopy++; + nLenCopy--; + } + while (nLenCopy); + if (pInfo != nullptr) { + *pInfo = 0; + } + RTL_LOG_STRING_NEW( *ppThis ); + return; + } +retry: + { + rtl_uString* pTemp; + rtl_uString* pTemp2 = nullptr; + rtl_TextToUnicodeConverter hConverter; + sal_uInt32 nInfo; + sal_Size nSrcBytes; + sal_Size nDestChars; + sal_Size nNewLen; + + /* Optimization for UTF-8 - we try to calculate the exact length */ + /* For all other encoding we try the maximum - and reallocate + the buffer if needed */ + if ( eTextEncoding == RTL_TEXTENCODING_UTF8 ) + { + bool ascii; + nNewLen = rtl_ImplGetFastUTF8UnicodeLen( pStr, nLen, &ascii ); + /* Includes the string only ASCII, then we could copy + the buffer faster */ + if ( ascii ) + { + sal_Unicode* pBuffer; + *ppThis = rtl_uString_ImplAlloc( nLen ); + if (*ppThis == nullptr) + { + if (pInfo != nullptr) { + *pInfo = RTL_TEXTTOUNICODE_INFO_ERROR | + RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL; + } + return; + } + pBuffer = (*ppThis)->buffer; + do + { + assert((static_cast<unsigned char>(*pStr)) <= 127); + *pBuffer = *pStr; + pBuffer++; + pStr++; + nLen--; + } + while ( nLen ); + if (pInfo != nullptr) { + *pInfo = 0; + } + RTL_LOG_STRING_NEW( *ppThis ); + return; + } + } + else + nNewLen = nLen; + + nCvtFlags |= RTL_TEXTTOUNICODE_FLAGS_FLUSH; + hConverter = rtl_createTextToUnicodeConverter( eTextEncoding ); + + pTemp = rtl_uString_ImplAlloc( nNewLen ); + if (pTemp == nullptr) { + if (pInfo != nullptr) { + *pInfo = RTL_TEXTTOUNICODE_INFO_ERROR | + RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL; + } + return; + } + nDestChars = rtl_convertTextToUnicode( hConverter, nullptr, + pStr, nLen, + pTemp->buffer, nNewLen, + nCvtFlags, + &nInfo, &nSrcBytes ); + + /* Buffer not big enough, try again with enough space */ + /* Shouldn't be the case, but if we get textencoding which + could results in more unicode characters we have this + code here. Could be the case for apple encodings */ + while ( nInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL ) + { + rtl_freeString( pTemp ); + nNewLen += 8; + pTemp = rtl_uString_ImplAlloc( nNewLen ); + if (pTemp == nullptr) { + if (pInfo != nullptr) { + *pInfo = RTL_TEXTTOUNICODE_INFO_ERROR | + RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL; + } + return; + } + nDestChars = rtl_convertTextToUnicode( hConverter, nullptr, + pStr, nLen, + pTemp->buffer, nNewLen, + nCvtFlags, + &nInfo, &nSrcBytes ); + } + + if (pInfo) + *pInfo = nInfo; + + /* Set the buffer to the correct size or if there is too + much overhead, reallocate to the correct size */ + if ( nNewLen > nDestChars+8 ) + { + pTemp2 = rtl_uString_ImplAlloc( nDestChars ); + } + if (pTemp2 != nullptr) + { + rtl::str::Copy(pTemp2->buffer, pTemp->buffer, nDestChars); + rtl_freeString(pTemp); + pTemp = pTemp2; + } + else + { + pTemp->length = nDestChars; + pTemp->buffer[nDestChars] = 0; + } + + rtl_destroyTextToUnicodeConverter( hConverter ); + *ppThis = pTemp; + + /* Results the conversion in an empty buffer - + create an empty string */ + if ( pTemp && !nDestChars ) + rtl_uString_new( ppThis ); + } + } + RTL_LOG_STRING_NEW( *ppThis ); +} + +void SAL_CALL rtl_string2UString( rtl_uString** ppThis, + const char* pStr, + sal_Int32 nLen, + rtl_TextEncoding eTextEncoding, + sal_uInt32 nCvtFlags ) SAL_THROW_EXTERN_C() +{ + assert(ppThis); + assert(nLen >= 0); + rtl_string2UString_status( ppThis, pStr, nLen, eTextEncoding, + nCvtFlags, nullptr ); +} + +/* ----------------------------------------------------------------------- */ + +namespace { + +enum StrLifecycle { + CANNOT_RETURN, + CAN_RETURN = 1 +}; + +} + +static oslMutex +getInternMutex() +{ + static oslMutex pPoolGuard = osl_createMutex(); + + return pPoolGuard; +} + +/* returns true if we found a dup in the pool */ +static void rtl_ustring_intern_internal( rtl_uString ** newStr, + rtl_uString * str, + StrLifecycle can_return ) +{ + oslMutex pPoolMutex; + + pPoolMutex = getInternMutex(); + + osl_acquireMutex( pPoolMutex ); + + *newStr = rtl_str_hash_intern (str, can_return); + + osl_releaseMutex( pPoolMutex ); + + RTL_LOG_STRING_INTERN_NEW(*newStr, str); + + if( can_return && *newStr != str ) + { /* we dupped, then found a match */ + rtl_freeString( str ); + } +} + +void SAL_CALL rtl_uString_intern( rtl_uString ** newStr, + rtl_uString * str) SAL_THROW_EXTERN_C() +{ + assert(newStr); + assert(str); + if (SAL_STRING_IS_INTERN(str)) + { + rtl::str::acquire(str); + *newStr = str; + } + else + { + rtl_uString *pOrg = *newStr; + *newStr = nullptr; + rtl_ustring_intern_internal( newStr, str, CANNOT_RETURN ); + if (pOrg) + rtl_uString_release (pOrg); + } +} + +static int rtl_canGuessUOutputLength( int len, rtl_TextEncoding eTextEncoding ) +{ + // FIXME: Maybe we should use a bit flag in the higher bits of the + // eTextEncoding value itself to determine the encoding type. But if we + // do, be sure to mask the value in certain places that expect the values + // to be numbered serially from 0 and up. One such place is + // Impl_getTextEncodingData(). + + switch ( eTextEncoding ) + { + // 1 to 1 (with no zero elements) + case RTL_TEXTENCODING_IBM_437: + case RTL_TEXTENCODING_IBM_850: + case RTL_TEXTENCODING_IBM_860: + case RTL_TEXTENCODING_IBM_861: + case RTL_TEXTENCODING_IBM_863: + case RTL_TEXTENCODING_IBM_865: + return len; + } + return 0; +} + +void SAL_CALL rtl_uString_internConvert( rtl_uString ** newStr, + const char * str, + sal_Int32 len, + rtl_TextEncoding eTextEncoding, + sal_uInt32 convertFlags, + sal_uInt32 * pInfo ) + SAL_THROW_EXTERN_C() +{ + assert(newStr); + assert(len >= 0); + rtl_uString *scratch; + + if (*newStr) + { + rtl_uString_release (*newStr); + *newStr = nullptr; + } + + if ( len < 256 ) + { // try various optimisations + sal_Int32 ulen; + if ( eTextEncoding == RTL_TEXTENCODING_ASCII_US ) + { + int i; + rtl_uString *pScratch; + pScratch = static_cast< rtl_uString * >( + alloca(sizeof (rtl_uString) + len * sizeof (sal_Unicode))); + for (i = 0; i < len; i++) + { + /* Check ASCII range */ + SAL_WARN_IF( !rtl::isAscii(static_cast<unsigned char>(str[i])), "rtl.string", + "rtl_ustring_internConvert() - Found char > 127 and RTL_TEXTENCODING_ASCII_US is specified" ); + pScratch->buffer[i] = str[i]; + } + pScratch->length = len; + rtl_ustring_intern_internal( newStr, pScratch, CANNOT_RETURN ); + return; + } + if ( (ulen = rtl_canGuessUOutputLength(len, eTextEncoding)) != 0 ) + { + rtl_uString *pScratch; + rtl_TextToUnicodeConverter hConverter; + sal_Size nSrcBytes; + sal_uInt32 nInfo; + + pScratch = static_cast< rtl_uString * >( + alloca( + sizeof (rtl_uString) + ulen * sizeof (sal_Unicode))); + + hConverter = rtl_createTextToUnicodeConverter( eTextEncoding ); + rtl_convertTextToUnicode( + hConverter, nullptr, str, len, pScratch->buffer, ulen, convertFlags, &nInfo, &nSrcBytes ); + rtl_destroyTextToUnicodeConverter( hConverter ); + + if (pInfo) + *pInfo = nInfo; + + pScratch->length = ulen; + rtl_ustring_intern_internal( newStr, pScratch, CANNOT_RETURN ); + return; + } + + /* FIXME: we want a nice UTF-8 / alloca shortcut here */ + } + + scratch = nullptr; + rtl_string2UString_status( &scratch, str, len, eTextEncoding, convertFlags, + pInfo ); + if (!scratch) { + return; + } + rtl_ustring_intern_internal( newStr, scratch, CAN_RETURN ); +} + +void internRelease (rtl_uString *pThis) +{ + rtl_uString *pFree = nullptr; + if ( SAL_STRING_REFCOUNT( + osl_atomic_decrement( &(pThis->refCount) ) ) == 0) + { + RTL_LOG_STRING_INTERN_DELETE(pThis); + oslMutex pPoolMutex = getInternMutex(); + osl_acquireMutex( pPoolMutex ); + + rtl_str_hash_remove (pThis); + + /* May have been separately acquired */ + if ( SAL_STRING_REFCOUNT( + osl_atomic_increment( &(pThis->refCount) ) ) == 1 ) + { + /* we got the last ref */ + pFree = pThis; + } + else /* very unusual */ + { + internRelease (pThis); + } + + osl_releaseMutex( pPoolMutex ); + } + if (pFree) + rtl_freeString (pFree); +} + +sal_uInt32 SAL_CALL rtl_uString_iterateCodePoints( + rtl_uString const * string, sal_Int32 * indexUtf16, + sal_Int32 incrementCodePoints) +{ + sal_Int32 n; + sal_Unicode cu; + sal_uInt32 cp; + assert(string != nullptr && indexUtf16 != nullptr); + n = *indexUtf16; + assert(n >= 0 && n <= string->length); + while (incrementCodePoints < 0) { + assert(n > 0); + cu = string->buffer[--n]; + if (rtl::isLowSurrogate(cu) && n != 0 && + rtl::isHighSurrogate(string->buffer[n - 1])) + { + --n; + } + ++incrementCodePoints; + } + assert(n >= 0 && n < string->length); + cu = string->buffer[n]; + if (rtl::isHighSurrogate(cu) && string->length - n >= 2 && + rtl::isLowSurrogate(string->buffer[n + 1])) + { + cp = rtl::combineSurrogates(cu, string->buffer[n + 1]); + } else { + cp = cu; + } + while (incrementCodePoints > 0) { + assert(n < string->length); + cu = string->buffer[n++]; + if (rtl::isHighSurrogate(cu) && n != string->length && + rtl::isLowSurrogate(string->buffer[n])) + { + ++n; + } + --incrementCodePoints; + } + assert(n >= 0 && n <= string->length); + *indexUtf16 = n; + return cp; +} + +sal_Bool rtl_convertStringToUString( + rtl_uString ** target, char const * source, sal_Int32 length, + rtl_TextEncoding encoding, sal_uInt32 flags) SAL_THROW_EXTERN_C() +{ + assert(target); + assert(length >= 0); + sal_uInt32 info; + rtl_string2UString_status(target, source, length, encoding, flags, &info); + return (info & RTL_TEXTTOUNICODE_INFO_ERROR) == 0; +} + +void rtl_uString_newReplaceFirst( + rtl_uString ** newStr, rtl_uString * str, rtl_uString const * from, + rtl_uString const * to, sal_Int32 * index) SAL_THROW_EXTERN_C() +{ + assert(from != nullptr); + assert(to != nullptr); + rtl_uString_newReplaceFirstUtf16LUtf16L(newStr, str, from->buffer, from->length, to->buffer, + to->length, index); +} + +void rtl_uString_newReplaceFirstAsciiL( + rtl_uString ** newStr, rtl_uString * str, char const * from, + sal_Int32 fromLength, rtl_uString const * to, sal_Int32 * index) + SAL_THROW_EXTERN_C() +{ + assert(to != nullptr); + rtl_uString_newReplaceFirstAsciiLUtf16L(newStr, str, from, fromLength, to->buffer, to->length, + index); +} + +void rtl_uString_newReplaceFirstToAsciiL( + rtl_uString ** newStr, rtl_uString * str, rtl_uString const * from, + char const * to, sal_Int32 toLength, sal_Int32 * index) + SAL_THROW_EXTERN_C() +{ + assert(from != nullptr); + rtl_uString_newReplaceFirstUtf16LAsciiL(newStr, str, from->buffer, from->length, to, toLength, + index); +} + +void rtl_uString_newReplaceFirstAsciiLAsciiL( + rtl_uString ** newStr, rtl_uString * str, char const * from, + sal_Int32 fromLength, char const * to, sal_Int32 toLength, + sal_Int32 * index) SAL_THROW_EXTERN_C() +{ + assert(index != nullptr); + rtl::str::newReplaceFirst(newStr, str, from, fromLength, to, toLength, *index); +} + +void rtl_uString_newReplaceFirstAsciiLUtf16L( + rtl_uString ** newStr, rtl_uString * str, char const * from, + sal_Int32 fromLength, sal_Unicode const * to, sal_Int32 toLength, + sal_Int32 * index) SAL_THROW_EXTERN_C() +{ + assert(index != nullptr); + rtl::str::newReplaceFirst(newStr, str, from, fromLength, to, toLength, *index); +} + +void rtl_uString_newReplaceFirstUtf16LAsciiL( + rtl_uString ** newStr, rtl_uString * str, sal_Unicode const * from, + sal_Int32 fromLength, char const * to, sal_Int32 toLength, + sal_Int32 * index) SAL_THROW_EXTERN_C() +{ + assert(index != nullptr); + rtl::str::newReplaceFirst(newStr, str, from, fromLength, to, toLength, *index); +} + +void rtl_uString_newReplaceFirstUtf16LUtf16L( + rtl_uString ** newStr, rtl_uString * str, sal_Unicode const * from, + sal_Int32 fromLength, sal_Unicode const * to, sal_Int32 toLength, + sal_Int32 * index) SAL_THROW_EXTERN_C() +{ + assert(index != nullptr); + rtl::str::newReplaceFirst(newStr, str, from, fromLength, to, toLength, *index); +} + +void rtl_uString_newReplaceAll( + rtl_uString ** newStr, rtl_uString * str, rtl_uString const * from, + rtl_uString const * to) SAL_THROW_EXTERN_C() +{ + rtl_uString_newReplaceAllFromIndex( newStr, str, from, to, 0 ); +} + +void rtl_uString_newReplaceAllFromIndex( + rtl_uString ** newStr, rtl_uString * str, rtl_uString const * from, + rtl_uString const * to, sal_Int32 fromIndex) SAL_THROW_EXTERN_C() +{ + assert(to != nullptr); + assert(fromIndex >= 0 && fromIndex <= str->length); + rtl_uString_newReplaceAllFromIndexUtf16LUtf16L(newStr, str, from->buffer, from->length, + to->buffer, to->length, fromIndex); +} + +void rtl_uString_newReplaceAllAsciiL( + rtl_uString ** newStr, rtl_uString * str, char const * from, + sal_Int32 fromLength, rtl_uString const * to) SAL_THROW_EXTERN_C() +{ + assert(to != nullptr); + rtl_uString_newReplaceAllAsciiLUtf16L(newStr, str, from, fromLength, to->buffer, to->length); +} + +void rtl_uString_newReplaceAllToAsciiL( + rtl_uString ** newStr, rtl_uString * str, rtl_uString const * from, + char const * to, sal_Int32 toLength) SAL_THROW_EXTERN_C() +{ + assert(from != nullptr); + rtl_uString_newReplaceAllUtf16LAsciiL(newStr, str, from->buffer, from->length, to, toLength); +} + +void rtl_uString_newReplaceAllAsciiLAsciiL( + rtl_uString ** newStr, rtl_uString * str, char const * from, + sal_Int32 fromLength, char const * to, sal_Int32 toLength) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceAllFromIndex(newStr, str, from, fromLength, to, toLength, 0); +} + +void rtl_uString_newReplaceAllAsciiLUtf16L( + rtl_uString ** newStr, rtl_uString * str, char const * from, + sal_Int32 fromLength, sal_Unicode const * to, sal_Int32 toLength) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceAllFromIndex(newStr, str, from, fromLength, to, toLength, 0); +} + +void rtl_uString_newReplaceAllUtf16LAsciiL( + rtl_uString ** newStr, rtl_uString * str, sal_Unicode const * from, + sal_Int32 fromLength, char const * to, sal_Int32 toLength) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceAllFromIndex(newStr, str, from, fromLength, to, toLength, 0); +} + +void rtl_uString_newReplaceAllUtf16LUtf16L( + rtl_uString ** newStr, rtl_uString * str, sal_Unicode const * from, + sal_Int32 fromLength, sal_Unicode const * to, sal_Int32 toLength) + SAL_THROW_EXTERN_C() +{ + rtl_uString_newReplaceAllFromIndexUtf16LUtf16L(newStr, str, from, fromLength, to, toLength, 0); +} + +void rtl_uString_newReplaceAllFromIndexUtf16LUtf16L( + rtl_uString ** newStr, rtl_uString * str, sal_Unicode const * from, + sal_Int32 fromLength, sal_Unicode const * to, sal_Int32 toLength, sal_Int32 fromIndex) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceAllFromIndex(newStr, str, from, fromLength, to, toLength, fromIndex); +} + +sal_Int32 SAL_CALL rtl_ustr_getLength(const sal_Unicode* pStr) SAL_THROW_EXTERN_C() +{ + return rtl::str::getLength(pStr); +} + +sal_Int32 SAL_CALL rtl_ustr_compare(const sal_Unicode* pStr1, const sal_Unicode* pStr2) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::null_terminated(pStr1), rtl::str::null_terminated(pStr2), + rtl::str::CompareNormal(), rtl::str::noShortening); +} + +sal_Int32 SAL_CALL rtl_ustr_compare_WithLength(const sal_Unicode* pStr1, sal_Int32 nStr1Len, + const sal_Unicode* pStr2, sal_Int32 nStr2Len) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len), + rtl::str::with_length(pStr2, nStr2Len), rtl::str::CompareNormal(), + rtl::str::noShortening); +} + +sal_Int32 SAL_CALL rtl_ustr_shortenedCompare_WithLength( + const sal_Unicode* pStr1, sal_Int32 nStr1Len, const sal_Unicode* pStr2, sal_Int32 nStr2Len, + sal_Int32 nShortenedLength) SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len), + rtl::str::with_length(pStr2, nStr2Len), rtl::str::CompareNormal(), + nShortenedLength); +} + +sal_Int32 SAL_CALL rtl_ustr_reverseCompare_WithLength(const sal_Unicode* pStr1, sal_Int32 nStr1Len, + const sal_Unicode* pStr2, sal_Int32 nStr2Len) + SAL_THROW_EXTERN_C() +{ + return rtl::str::reverseCompare_WithLengths(pStr1, nStr1Len, pStr2, nStr2Len, + rtl::str::CompareNormal()); +} + +sal_Int32 SAL_CALL rtl_ustr_compareIgnoreAsciiCase(const sal_Unicode* pStr1, + const sal_Unicode* pStr2) SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::null_terminated(pStr1), rtl::str::null_terminated(pStr2), + rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening); +} + +sal_Int32 SAL_CALL rtl_ustr_compareIgnoreAsciiCase_WithLength(const sal_Unicode* pStr1, + sal_Int32 nStr1Len, + const sal_Unicode* pStr2, + sal_Int32 nStr2Len) + SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len), + rtl::str::with_length(pStr2, nStr2Len), + rtl::str::CompareIgnoreAsciiCase(), rtl::str::noShortening); +} + +sal_Int32 SAL_CALL rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( + const sal_Unicode* pStr1, sal_Int32 nStr1Len, const sal_Unicode* pStr2, sal_Int32 nStr2Len, + sal_Int32 nShortenedLength) SAL_THROW_EXTERN_C() +{ + return rtl::str::compare(rtl::str::with_length(pStr1, nStr1Len), + rtl::str::with_length(pStr2, nStr2Len), + rtl::str::CompareIgnoreAsciiCase(), nShortenedLength); +} + +sal_Int32 SAL_CALL rtl_ustr_hashCode(const sal_Unicode* pStr) SAL_THROW_EXTERN_C() +{ + return rtl::str::hashCode(pStr); +} + +sal_Int32 SAL_CALL rtl_ustr_hashCode_WithLength(const sal_Unicode* pStr, sal_Int32 nLen) + SAL_THROW_EXTERN_C() +{ + return rtl::str::hashCode_WithLength(pStr, nLen); +} + +sal_Int32 SAL_CALL rtl_ustr_indexOfChar(const sal_Unicode* pStr, sal_Unicode c) SAL_THROW_EXTERN_C() +{ + return rtl::str::indexOfChar(pStr, c); +} + +sal_Int32 SAL_CALL rtl_ustr_indexOfChar_WithLength(const sal_Unicode* pStr, sal_Int32 nLen, + sal_Unicode c) SAL_THROW_EXTERN_C() +{ + return rtl::str::indexOfChar_WithLength(pStr, nLen, c); +} + +sal_Int32 SAL_CALL rtl_ustr_lastIndexOfChar(const sal_Unicode* pStr, sal_Unicode c) + SAL_THROW_EXTERN_C() +{ + return rtl::str::lastIndexOfChar(pStr, c); +} + +sal_Int32 SAL_CALL rtl_ustr_lastIndexOfChar_WithLength(const sal_Unicode* pStr, sal_Int32 nLen, + sal_Unicode c) SAL_THROW_EXTERN_C() +{ + return rtl::str::lastIndexOfChar_WithLength(pStr, nLen, c); +} + +sal_Int32 SAL_CALL rtl_ustr_indexOfStr(const sal_Unicode* pStr, const sal_Unicode* pSubStr) + SAL_THROW_EXTERN_C() +{ + return rtl::str::indexOfStr(pStr, pSubStr); +} + +sal_Int32 SAL_CALL rtl_ustr_indexOfStr_WithLength(const sal_Unicode* pStr, sal_Int32 nStrLen, + const sal_Unicode* pSubStr, sal_Int32 nSubLen) + SAL_THROW_EXTERN_C() +{ + return rtl::str::indexOfStr_WithLength(pStr, nStrLen, pSubStr, nSubLen); +} + +sal_Int32 SAL_CALL rtl_ustr_lastIndexOfStr(const sal_Unicode* pStr, const sal_Unicode* pSubStr) + SAL_THROW_EXTERN_C() +{ + return rtl::str::lastIndexOfStr(pStr, pSubStr); +} + +sal_Int32 SAL_CALL rtl_ustr_lastIndexOfStr_WithLength(const sal_Unicode* pStr, sal_Int32 nStrLen, + const sal_Unicode* pSubStr, sal_Int32 nSubLen) + SAL_THROW_EXTERN_C() +{ + return rtl::str::lastIndexOfStr_WithLength(pStr, nStrLen, pSubStr, nSubLen); +} + +void SAL_CALL rtl_ustr_replaceChar(sal_Unicode* pStr, sal_Unicode cOld, sal_Unicode cNew) + SAL_THROW_EXTERN_C() +{ + return rtl::str::replaceChars(rtl::str::null_terminated(pStr), rtl::str::FromTo(cOld, cNew)); +} + +void SAL_CALL rtl_ustr_replaceChar_WithLength(sal_Unicode* pStr, sal_Int32 nLen, sal_Unicode cOld, + sal_Unicode cNew) SAL_THROW_EXTERN_C() +{ + return rtl::str::replaceChars(rtl::str::with_length(pStr, nLen), rtl::str::FromTo(cOld, cNew)); +} + +void SAL_CALL rtl_ustr_toAsciiLowerCase(sal_Unicode* pStr) SAL_THROW_EXTERN_C() +{ + return rtl::str::replaceChars(rtl::str::null_terminated(pStr), rtl::str::toAsciiLower); +} + +void SAL_CALL rtl_ustr_toAsciiLowerCase_WithLength(sal_Unicode* pStr, sal_Int32 nLen) + SAL_THROW_EXTERN_C() +{ + return rtl::str::replaceChars(rtl::str::with_length(pStr, nLen), rtl::str::toAsciiLower); +} + +void SAL_CALL rtl_ustr_toAsciiUpperCase(sal_Unicode* pStr) SAL_THROW_EXTERN_C() +{ + return rtl::str::replaceChars(rtl::str::null_terminated(pStr), rtl::str::toAsciiUpper); +} + +void SAL_CALL rtl_ustr_toAsciiUpperCase_WithLength(sal_Unicode* pStr, sal_Int32 nLen) + SAL_THROW_EXTERN_C() +{ + return rtl::str::replaceChars(rtl::str::with_length(pStr, nLen), rtl::str::toAsciiUpper); +} + +sal_Int32 SAL_CALL rtl_ustr_trim(sal_Unicode* pStr) SAL_THROW_EXTERN_C() +{ + return rtl::str::trim(pStr); +} + +sal_Int32 SAL_CALL rtl_ustr_trim_WithLength(sal_Unicode* pStr, sal_Int32 nLen) SAL_THROW_EXTERN_C() +{ + return rtl::str::trim_WithLength(pStr, nLen); +} + +sal_Int32 SAL_CALL rtl_ustr_valueOfBoolean(sal_Unicode* pStr, sal_Bool b) SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfBoolean(pStr, b); +} + +sal_Int32 SAL_CALL rtl_ustr_valueOfChar(sal_Unicode* pStr, sal_Unicode c) SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfChar(pStr, c); +} + +sal_Int32 SAL_CALL rtl_ustr_valueOfInt32(sal_Unicode* pStr, sal_Int32 n, sal_Int16 nRadix) + SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfInt<RTL_USTR_MAX_VALUEOFINT32>(pStr, n, nRadix); +} + +sal_Int32 SAL_CALL rtl_ustr_valueOfInt64(sal_Unicode* pStr, sal_Int64 n, sal_Int16 nRadix) + SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfInt<RTL_USTR_MAX_VALUEOFINT64>(pStr, n, nRadix); +} + +sal_Int32 SAL_CALL rtl_ustr_valueOfUInt64(sal_Unicode* pStr, sal_uInt64 n, sal_Int16 nRadix) + SAL_THROW_EXTERN_C() +{ + return rtl::str::valueOfInt<RTL_USTR_MAX_VALUEOFUINT64>(pStr, n, nRadix); +} + +sal_Bool SAL_CALL rtl_ustr_toBoolean(const sal_Unicode* pStr) SAL_THROW_EXTERN_C() +{ + return rtl::str::toBoolean(pStr); +} + +sal_Int32 SAL_CALL rtl_ustr_toInt32(const sal_Unicode* pStr, sal_Int16 nRadix) SAL_THROW_EXTERN_C() +{ + return rtl::str::toInt<sal_Int32>(rtl::str::null_terminated(pStr), nRadix); +} + +sal_Int64 SAL_CALL rtl_ustr_toInt64(const sal_Unicode* pStr, sal_Int16 nRadix) SAL_THROW_EXTERN_C() +{ + return rtl::str::toInt<sal_Int64>(rtl::str::null_terminated(pStr), nRadix); +} + +sal_Int64 SAL_CALL rtl_ustr_toInt64_WithLength(const sal_Unicode* pStr, sal_Int16 nRadix, + sal_Int32 nStrLength) SAL_THROW_EXTERN_C() +{ + return rtl::str::toInt<sal_Int64>(rtl::str::with_length(pStr, nStrLength), nRadix); +} + +sal_uInt32 SAL_CALL rtl_ustr_toUInt32(const sal_Unicode* pStr, sal_Int16 nRadix) + SAL_THROW_EXTERN_C() +{ + return rtl::str::toInt<sal_uInt32>(rtl::str::null_terminated(pStr), nRadix); +} + +sal_uInt64 SAL_CALL rtl_ustr_toUInt64(const sal_Unicode* pStr, sal_Int16 nRadix) + SAL_THROW_EXTERN_C() +{ + return rtl::str::toInt<sal_uInt64>(rtl::str::null_terminated(pStr), nRadix); +} + +rtl_uString* rtl_uString_ImplAlloc(sal_Int32 nLen) +{ + return rtl::str::Alloc<rtl_uString>(nLen); +} + +void SAL_CALL rtl_uString_acquire(rtl_uString* pThis) SAL_THROW_EXTERN_C() +{ + return rtl::str::acquire(pThis); +} + +void SAL_CALL rtl_uString_release(rtl_uString* pThis) SAL_THROW_EXTERN_C() +{ + return rtl::str::release(pThis); +} + +void SAL_CALL rtl_uString_new(rtl_uString** ppThis) SAL_THROW_EXTERN_C() +{ + return rtl::str::new_(ppThis); +} + +rtl_uString* SAL_CALL rtl_uString_alloc(sal_Int32 nLen) SAL_THROW_EXTERN_C() +{ + assert(nLen >= 0); + return rtl::str::Alloc<rtl_uString>(nLen); +} + +void SAL_CALL rtl_uString_new_WithLength(rtl_uString** ppThis, sal_Int32 nLen) SAL_THROW_EXTERN_C() +{ + rtl::str::new_WithLength(ppThis, nLen); +} + +void SAL_CALL rtl_uString_newFromString(rtl_uString** ppThis, const rtl_uString* pStr) + SAL_THROW_EXTERN_C() +{ + rtl::str::newFromString(ppThis, pStr); +} + +void SAL_CALL rtl_uString_newFromStr(rtl_uString** ppThis, const sal_Unicode* pCharStr) + SAL_THROW_EXTERN_C() +{ + rtl::str::newFromStr(ppThis, pCharStr); +} + +void SAL_CALL rtl_uString_newFromStr_WithLength(rtl_uString** ppThis, const sal_Unicode* pCharStr, + sal_Int32 nLen) SAL_THROW_EXTERN_C() +{ + rtl::str::newFromStr_WithLength(ppThis, pCharStr, nLen); +} + +void SAL_CALL rtl_uString_newFromSubString(rtl_uString** ppThis, const rtl_uString* pFrom, + sal_Int32 beginIndex, sal_Int32 count) + SAL_THROW_EXTERN_C() +{ + rtl::str::newFromSubString(ppThis, pFrom, beginIndex, count); +} + +// Used when creating from string literals. +void SAL_CALL rtl_uString_newFromLiteral(rtl_uString** ppThis, const char* pCharStr, sal_Int32 nLen, + sal_Int32 allocExtra) SAL_THROW_EXTERN_C() +{ + rtl::str::newFromStr_WithLength(ppThis, pCharStr, nLen, allocExtra); +} + +void SAL_CALL rtl_uString_assign(rtl_uString** ppThis, rtl_uString* pStr) SAL_THROW_EXTERN_C() +{ + rtl::str::assign(ppThis, pStr); +} + +sal_Int32 SAL_CALL rtl_uString_getLength(const rtl_uString* pThis) SAL_THROW_EXTERN_C() +{ + return rtl::str::getLength(pThis); +} + +sal_Unicode* SAL_CALL rtl_uString_getStr(rtl_uString* pThis) SAL_THROW_EXTERN_C() +{ + return rtl::str::getStr(pThis); +} + +void SAL_CALL rtl_uString_newConcat(rtl_uString** ppThis, rtl_uString* pLeft, rtl_uString* pRight) + SAL_THROW_EXTERN_C() +{ + rtl::str::newConcat(ppThis, pLeft, pRight); +} + +void SAL_CALL rtl_uString_ensureCapacity(rtl_uString** ppThis, sal_Int32 size) SAL_THROW_EXTERN_C() +{ + rtl::str::ensureCapacity(ppThis, size); +} + +void SAL_CALL rtl_uString_newReplaceStrAt(rtl_uString** ppThis, rtl_uString* pStr, sal_Int32 nIndex, + sal_Int32 nCount, rtl_uString* pNewSubStr) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceStrAt(ppThis, pStr, nIndex, nCount, pNewSubStr); +} + +void SAL_CALL rtl_uString_newReplaceStrAtUtf16L(rtl_uString** ppThis, rtl_uString* pStr, sal_Int32 nIndex, + sal_Int32 nCount, sal_Unicode const * subStr, sal_Int32 substrLen) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceStrAt(ppThis, pStr, nIndex, nCount, subStr, substrLen); +} + +void SAL_CALL rtl_uString_newReplace(rtl_uString** ppThis, rtl_uString* pStr, sal_Unicode cOld, + sal_Unicode cNew) SAL_THROW_EXTERN_C() +{ + rtl::str::newReplace(ppThis, pStr, cOld, cNew); +} + +void SAL_CALL rtl_uString_newToAsciiLowerCase(rtl_uString** ppThis, rtl_uString* pStr) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceChars<rtl::str::ToAsciiLower>(ppThis, pStr); +} + +void SAL_CALL rtl_uString_newToAsciiUpperCase(rtl_uString** ppThis, rtl_uString* pStr) + SAL_THROW_EXTERN_C() +{ + rtl::str::newReplaceChars<rtl::str::ToAsciiUpper>(ppThis, pStr); +} + +void SAL_CALL rtl_uString_newTrim(rtl_uString** ppThis, rtl_uString* pStr) SAL_THROW_EXTERN_C() +{ + rtl::str::newTrim(ppThis, pStr); +} + +sal_Int32 SAL_CALL rtl_uString_getToken(rtl_uString** ppThis, rtl_uString* pStr, sal_Int32 nToken, + sal_Unicode cTok, sal_Int32 nIndex) SAL_THROW_EXTERN_C() +{ + return rtl::str::getToken(ppThis, pStr, nToken, cTok, nIndex); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/rtl/uuid.cxx b/sal/rtl/uuid.cxx new file mode 100644 index 000000000..130be1245 --- /dev/null +++ b/sal/rtl/uuid.cxx @@ -0,0 +1,173 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <string.h> +#include <stdlib.h> + +#include <osl/mutex.hxx> +#include <rtl/random.h> +#include <rtl/uuid.h> +#include <rtl/digest.h> + +#define SWAP_INT32_TO_NETWORK(x)\ + { sal_uInt32 y = x;\ + sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(&(x)); \ + p[0] = static_cast<sal_uInt8>( ( y >> 24 ) & 0xff );\ + p[1] = static_cast<sal_uInt8>( ( y >> 16 ) & 0xff );\ + p[2] = static_cast<sal_uInt8>( ( y >> 8 ) & 0xff );\ + p[3] = static_cast<sal_uInt8>( ( y ) & 0xff);\ + } +#define SWAP_INT16_TO_NETWORK(x)\ + { sal_uInt16 y = x;\ + sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(&(x)); \ + p[0] = static_cast<sal_uInt8>( ( y >> 8 ) & 0xff );\ + p[1] = static_cast<sal_uInt8>( ( y ) & 0xff);\ + } + +#define SWAP_NETWORK_TO_INT16(x)\ + { sal_uInt16 y = x;\ + sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(&(y));\ + x = ( ( (static_cast<sal_uInt16>(p[0])) & 0xff) << 8 ) |\ + ( ( static_cast<sal_uInt16>(p[1])) & 0xff);\ + } +#define SWAP_NETWORK_TO_INT32(x)\ + { sal_uInt32 y = x;\ + sal_uInt8 *p = reinterpret_cast<sal_uInt8 *>(&(y)); \ + x = ( ( (static_cast<sal_uInt32>(p[0])) & 0xff) << 24 ) |\ + ( ( (static_cast<sal_uInt32>(p[1])) & 0xff) << 16 ) |\ + ( ( (static_cast<sal_uInt32>(p[2])) & 0xff) << 8 ) |\ + ( ( static_cast<sal_uInt32>(p[3])) & 0xff);\ + } + +namespace { + +struct UUID +{ + sal_uInt32 time_low; + sal_uInt16 time_mid; + sal_uInt16 time_hi_and_version; + sal_uInt8 clock_seq_hi_and_reserved; + sal_uInt8 clock_seq_low; + sal_uInt8 node[6]; +}; + +} + +static void write_v3( sal_uInt8 *pUuid ) +{ + UUID uuid; + // copy to avoid alignment problems + memcpy(&uuid, pUuid, 16); + + SWAP_NETWORK_TO_INT32(uuid.time_low); + SWAP_NETWORK_TO_INT16(uuid.time_mid); + SWAP_NETWORK_TO_INT16(uuid.time_hi_and_version); + + /* put in the variant and version bits */ + uuid.time_hi_and_version &= 0x0FFF; + uuid.time_hi_and_version |= (3 << 12); + uuid.clock_seq_hi_and_reserved &= 0x3F; + uuid.clock_seq_hi_and_reserved |= 0x80; + + SWAP_INT32_TO_NETWORK(uuid.time_low); + SWAP_INT16_TO_NETWORK(uuid.time_mid); + SWAP_INT16_TO_NETWORK(uuid.time_hi_and_version); + + memcpy(pUuid, &uuid, 16); +} + +extern "C" void SAL_CALL rtl_createUuid(sal_uInt8 *pTargetUUID , + SAL_UNUSED_PARAMETER const sal_uInt8 *, + SAL_UNUSED_PARAMETER sal_Bool) +{ + { + static rtlRandomPool pool = []() { + rtlRandomPool aPool = rtl_random_createPool(); + if (!aPool) + { + abort(); + // only possible way to signal failure here (rtl_createUuid + // being part of a fixed C API) + } + return aPool; + }(); + + osl::MutexGuard g(osl::Mutex::getGlobalMutex()); + if (rtl_random_getBytes(pool, pTargetUUID, 16) != rtl_Random_E_None) + { + abort(); + // only possible way to signal failure here (rtl_createUuid + // being part of a fixed C API) + } + } + // See ITU-T Recommendation X.667: + pTargetUUID[6] &= 0x0F; + pTargetUUID[6] |= 0x40; + pTargetUUID[8] &= 0x3F; + pTargetUUID[8] |= 0x80; +} + +extern "C" void SAL_CALL rtl_createNamedUuid(sal_uInt8 *pTargetUUID, + const sal_uInt8 *pNameSpaceUUID, + const rtl_String *pName ) +{ + rtlDigest digest = rtl_digest_createMD5(); + + rtl_digest_updateMD5(digest, pNameSpaceUUID, 16); + rtl_digest_updateMD5(digest, pName->buffer, pName->length); + + rtl_digest_getMD5(digest, pTargetUUID, 16); + rtl_digest_destroyMD5(digest); + + write_v3(pTargetUUID); +} + +extern "C" sal_Int32 SAL_CALL rtl_compareUuid(const sal_uInt8 *pUUID1, const sal_uInt8 *pUUID2) +{ + int i; + UUID u1; + UUID u2; + memcpy(&u1, pUUID1, 16 ); + memcpy(&u2, pUUID2, 16 ); + + SWAP_NETWORK_TO_INT32(u1.time_low); + SWAP_NETWORK_TO_INT16(u1.time_mid); + SWAP_NETWORK_TO_INT16(u1.time_hi_and_version); + + SWAP_NETWORK_TO_INT32(u2.time_low); + SWAP_NETWORK_TO_INT16(u2.time_mid); + SWAP_NETWORK_TO_INT16(u2.time_hi_and_version); + +#define CHECK(f1, f2) if (f1 != f2) return f1 < f2 ? -1 : 1; + CHECK(u1.time_low, u2.time_low); + CHECK(u1.time_mid, u2.time_mid); + CHECK(u1.time_hi_and_version, u2.time_hi_and_version); + CHECK(u1.clock_seq_hi_and_reserved, u2.clock_seq_hi_and_reserved); + CHECK(u1.clock_seq_low, u2.clock_seq_low); + for (i = 0; i < 6; i++) + { + if (u1.node[i] < u2.node[i]) + return -1; + if (u1.node[i] > u2.node[i]) + return 1; + } + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |