diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-03-26 19:21:20 +0000 |
---|---|---|
committer | <> | 2014-05-08 15:03:54 +0000 |
commit | fb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch) | |
tree | c2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/Runtime/common/string | |
parent | 58ed4748338f9466599adfc8a9171280ed99e23f (diff) | |
download | VirtualBox-master.tar.gz |
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.3.10.tar.bz2.HEADVirtualBox-4.3.10master
Diffstat (limited to 'src/VBox/Runtime/common/string')
52 files changed, 1372 insertions, 180 deletions
diff --git a/src/VBox/Runtime/common/string/RTStrCmp.cpp b/src/VBox/Runtime/common/string/RTStrCmp.cpp index 779675f0..598a1869 100644 --- a/src/VBox/Runtime/common/string/RTStrCmp.cpp +++ b/src/VBox/Runtime/common/string/RTStrCmp.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2009 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/RTStrNCmp.cpp b/src/VBox/Runtime/common/string/RTStrNCmp.cpp index 5070218a..5880c8ff 100644 --- a/src/VBox/Runtime/common/string/RTStrNCmp.cpp +++ b/src/VBox/Runtime/common/string/RTStrNCmp.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2009 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -41,7 +41,22 @@ RTDECL(int) RTStrNCmp(const char *psz1, const char *psz2, size_t cchMax) if (!psz2) return 1; +#ifdef RT_OS_SOLARIS + /* Solaris: tstUtf8 found to fail for some RTSTR_MAX on testboxsh1: + solaris.amd64 v5.10 (Generic_142901-12 (Assembled 30 March 2009)). */ + while (cchMax-- > 0) + { + char ch1 = *psz1++; + char ch2 = *psz2++; + if (ch1 != ch2) + return ch1 > ch2 ? 1 : -1; + else if (ch1 == 0) + break; + } + return 0; +#else return strncmp(psz1, psz2, cchMax); +#endif } RT_EXPORT_SYMBOL(RTStrNCmp); diff --git a/src/VBox/Runtime/common/string/RTStrNLen.cpp b/src/VBox/Runtime/common/string/RTStrNLen.cpp index d30c9ed5..777c7509 100644 --- a/src/VBox/Runtime/common/string/RTStrNLen.cpp +++ b/src/VBox/Runtime/common/string/RTStrNLen.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/RTStrNLenEx.cpp b/src/VBox/Runtime/common/string/RTStrNLenEx.cpp index 3764ce35..19e18152 100644 --- a/src/VBox/Runtime/common/string/RTStrNLenEx.cpp +++ b/src/VBox/Runtime/common/string/RTStrNLenEx.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/RTStrPrintHexBytes.cpp b/src/VBox/Runtime/common/string/RTStrPrintHexBytes.cpp index f00298e5..492f2f49 100644 --- a/src/VBox/Runtime/common/string/RTStrPrintHexBytes.cpp +++ b/src/VBox/Runtime/common/string/RTStrPrintHexBytes.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/RTStrStr.cpp b/src/VBox/Runtime/common/string/RTStrStr.cpp index 903826e1..a6555bf5 100644 --- a/src/VBox/Runtime/common/string/RTStrStr.cpp +++ b/src/VBox/Runtime/common/string/RTStrStr.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2009 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/base64.cpp b/src/VBox/Runtime/common/string/base64.cpp index 514d4474..96269644 100644 --- a/src/VBox/Runtime/common/string/base64.cpp +++ b/src/VBox/Runtime/common/string/base64.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memchr.asm b/src/VBox/Runtime/common/string/memchr.asm index 0ee2968d..95fd1654 100644 --- a/src/VBox/Runtime/common/string/memchr.asm +++ b/src/VBox/Runtime/common/string/memchr.asm @@ -4,7 +4,7 @@ ; ; -; Copyright (C) 2006-2007 Oracle Corporation +; Copyright (C) 2006-2010 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memchr.cpp b/src/VBox/Runtime/common/string/memchr.cpp index ba2de095..abfa379d 100644 --- a/src/VBox/Runtime/common/string/memchr.cpp +++ b/src/VBox/Runtime/common/string/memchr.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memchr_alias.c b/src/VBox/Runtime/common/string/memchr_alias.c index 0d009c7d..90929549 100644 --- a/src/VBox/Runtime/common/string/memchr_alias.c +++ b/src/VBox/Runtime/common/string/memchr_alias.c @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memcmp.asm b/src/VBox/Runtime/common/string/memcmp.asm index afadf923..5176dfed 100644 --- a/src/VBox/Runtime/common/string/memcmp.asm +++ b/src/VBox/Runtime/common/string/memcmp.asm @@ -4,7 +4,7 @@ ; ; -; Copyright (C) 2006-2007 Oracle Corporation +; Copyright (C) 2006-2010 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memcmp.cpp b/src/VBox/Runtime/common/string/memcmp.cpp index 3dea769e..6ccf9b1f 100644 --- a/src/VBox/Runtime/common/string/memcmp.cpp +++ b/src/VBox/Runtime/common/string/memcmp.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memcmp_alias.c b/src/VBox/Runtime/common/string/memcmp_alias.c index 30f78637..6a4337b8 100644 --- a/src/VBox/Runtime/common/string/memcmp_alias.c +++ b/src/VBox/Runtime/common/string/memcmp_alias.c @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memcpy.asm b/src/VBox/Runtime/common/string/memcpy.asm index 6f1bed22..b8f94df5 100644 --- a/src/VBox/Runtime/common/string/memcpy.asm +++ b/src/VBox/Runtime/common/string/memcpy.asm @@ -4,7 +4,7 @@ ; ; -; Copyright (C) 2006-2007 Oracle Corporation +; Copyright (C) 2006-2010 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memcpy.cpp b/src/VBox/Runtime/common/string/memcpy.cpp index 56ccdfb2..d04cfe04 100644 --- a/src/VBox/Runtime/common/string/memcpy.cpp +++ b/src/VBox/Runtime/common/string/memcpy.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memcpy_alias.c b/src/VBox/Runtime/common/string/memcpy_alias.c index 5ddc7b8c..ecea9ae3 100644 --- a/src/VBox/Runtime/common/string/memcpy_alias.c +++ b/src/VBox/Runtime/common/string/memcpy_alias.c @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memmove.asm b/src/VBox/Runtime/common/string/memmove.asm index b10ede77..c52d988e 100644 --- a/src/VBox/Runtime/common/string/memmove.asm +++ b/src/VBox/Runtime/common/string/memmove.asm @@ -4,7 +4,7 @@ ; ; -; Copyright (C) 2006-2008 Oracle Corporation +; Copyright (C) 2006-2010 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memmove_alias.c b/src/VBox/Runtime/common/string/memmove_alias.c index f30c9149..1fc2e0ce 100644 --- a/src/VBox/Runtime/common/string/memmove_alias.c +++ b/src/VBox/Runtime/common/string/memmove_alias.c @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2008 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/mempcpy.asm b/src/VBox/Runtime/common/string/mempcpy.asm index b47c4368..157a63db 100644 --- a/src/VBox/Runtime/common/string/mempcpy.asm +++ b/src/VBox/Runtime/common/string/mempcpy.asm @@ -4,7 +4,7 @@ ; ; -; Copyright (C) 2006-2007 Oracle Corporation +; Copyright (C) 2006-2010 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memset.asm b/src/VBox/Runtime/common/string/memset.asm index 96cc48ec..a2d562ba 100644 --- a/src/VBox/Runtime/common/string/memset.asm +++ b/src/VBox/Runtime/common/string/memset.asm @@ -4,7 +4,7 @@ ; ; -; Copyright (C) 2006-2007 Oracle Corporation +; Copyright (C) 2006-2010 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memset.cpp b/src/VBox/Runtime/common/string/memset.cpp index 9bf35893..9935bef5 100644 --- a/src/VBox/Runtime/common/string/memset.cpp +++ b/src/VBox/Runtime/common/string/memset.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/memset_alias.c b/src/VBox/Runtime/common/string/memset_alias.c index 49d4abd3..64793471 100644 --- a/src/VBox/Runtime/common/string/memset_alias.c +++ b/src/VBox/Runtime/common/string/memset_alias.c @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/simplepattern.cpp b/src/VBox/Runtime/common/string/simplepattern.cpp index f54d4ce0..6f6bdd2b 100644 --- a/src/VBox/Runtime/common/string/simplepattern.cpp +++ b/src/VBox/Runtime/common/string/simplepattern.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2008 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/straprintf.cpp b/src/VBox/Runtime/common/string/straprintf.cpp index a0ee0350..a09abd33 100644 --- a/src/VBox/Runtime/common/string/straprintf.cpp +++ b/src/VBox/Runtime/common/string/straprintf.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strcache.cpp b/src/VBox/Runtime/common/string/strcache.cpp index 642d5836..e452a4a7 100644 --- a/src/VBox/Runtime/common/string/strcache.cpp +++ b/src/VBox/Runtime/common/string/strcache.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -31,12 +31,93 @@ #include <iprt/strcache.h> #include "internal/iprt.h" +#include <iprt/alloca.h> #include <iprt/asm.h> #include <iprt/assert.h> +#include <iprt/critsect.h> #include <iprt/err.h> -#include <iprt/mempool.h> -#include <iprt/string.h> +#include <iprt/list.h> +#include <iprt/mem.h> #include <iprt/once.h> +#include <iprt/param.h> +#include <iprt/string.h> + +#include "internal/strhash.h" +#include "internal/magics.h" + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +/** Special NIL pointer for the hash table. It differs from NULL in that it is + * a valid hash table entry when doing a lookup. */ +#define PRTSTRCACHEENTRY_NIL ((PRTSTRCACHEENTRY)~(uintptr_t)1) + +/** Calcuates the increment when handling a collision. + * The current formula makes sure it's always odd so we cannot possibly end + * up a cyclic loop with an even sized table. It also takes more bits from + * the length part. */ +#define RTSTRCACHE_COLLISION_INCR(uHashLen) ( ((uHashLen >> 8) | 1) ) + +/** The initial hash table size. Must be power of two. */ +#define RTSTRCACHE_INITIAL_HASH_SIZE 512 +/** The hash table growth factor. */ +#define RTSTRCACHE_HASH_GROW_FACTOR 4 + +/** + * The RTSTRCACHEENTRY size threshold at which we stop using our own allocator + * and switch to the application heap, expressed as a power of two. + * + * Using a 1KB as a reasonable limit here. + */ +#ifdef RTSTRCACHE_WITH_MERGED_ALLOCATOR +# define RTSTRCACHE_HEAP_THRESHOLD_BIT 10 +#else +# define RTSTRCACHE_HEAP_THRESHOLD_BIT 9 +#endif +/** The RTSTRCACHE_HEAP_THRESHOLD_BIT as a byte limit. */ +#define RTSTRCACHE_HEAP_THRESHOLD RT_BIT_32(RTSTRCACHE_HEAP_THRESHOLD_BIT) +/** Big (heap) entry size alignment. */ +#define RTSTRCACHE_HEAP_ENTRY_SIZE_ALIGN 16 + +#ifdef RTSTRCACHE_WITH_MERGED_ALLOCATOR +/** + * The RTSTRCACHEENTRY size threshold at which we start using the merge free + * list for allocations, expressed as a power of two. + */ +# define RTSTRCACHE_MERGED_THRESHOLD_BIT 6 + +/** The number of bytes (power of two) that the merged allocation lists should + * be grown by. Must be much greater than RTSTRCACHE_MERGED_THRESHOLD. */ +# define RTSTRCACHE_MERGED_GROW_SIZE _32K +#endif + +/** The number of bytes (power of two) that the fixed allocation lists should + * be grown by. */ +#define RTSTRCACHE_FIXED_GROW_SIZE _32K + +/** The number of fixed sized lists. */ +#define RTSTRCACHE_NUM_OF_FIXED_SIZES 12 + + +/** Validates a string cache handle, translating RTSTRCACHE_DEFAULT when found, + * and returns rc if not valid. */ +#define RTSTRCACHE_VALID_RETURN_RC(pStrCache, rc) \ + do { \ + if ((pStrCache) == RTSTRCACHE_DEFAULT) \ + { \ + int rcOnce = RTOnce(&g_rtStrCacheOnce, rtStrCacheInitDefault, NULL); \ + if (RT_FAILURE(rcOnce)) \ + return (rc); \ + (pStrCache) = g_hrtStrCacheDefault; \ + } \ + else \ + { \ + AssertPtrReturn((pStrCache), (rc)); \ + AssertReturn((pStrCache)->u32Magic == RTSTRCACHE_MAGIC, (rc)); \ + } \ + } while (0) + /******************************************************************************* @@ -44,117 +125,790 @@ *******************************************************************************/ /** * String cache entry. - * - * Each entry is */ typedef struct RTSTRCACHEENTRY { /** The number of references. */ uint32_t volatile cRefs; - /** Offset into the chunk (bytes). */ - uint32_t offChunk; - /** The string length. */ - uint32_t cch; - /** The primary hash value. */ - uint32_t uHash1; + /** The lower 16-bit hash value. */ + uint16_t uHash; + /** The string length (excluding the terminator). + * If this is set to RTSTRCACHEENTRY_BIG_LEN, this is a BIG entry + * (RTSTRCACHEBIGENTRY). */ + uint16_t cchString; /** The string. */ - char szString[16]; + char szString[8]; } RTSTRCACHEENTRY; -AssertCompileSize(RTSTRCACHEENTRY, 32); +AssertCompileSize(RTSTRCACHEENTRY, 16); /** Pointer to a string cache entry. */ typedef RTSTRCACHEENTRY *PRTSTRCACHEENTRY; /** Pointer to a const string cache entry. */ typedef RTSTRCACHEENTRY *PCRTSTRCACHEENTRY; +/** RTSTCACHEENTRY::cchString value for big cache entries. */ +#define RTSTRCACHEENTRY_BIG_LEN UINT16_MAX + +/** + * Big string cache entry. + * + * These are allocated individually from the application heap. + */ +typedef struct RTSTRCACHEBIGENTRY +{ + /** List entry. */ + RTLISTNODE ListEntry; + /** The string length. */ + uint32_t cchString; + /** The full hash value / padding. */ + uint32_t uHash; + /** The core entry. */ + RTSTRCACHEENTRY Core; +} RTSTRCACHEBIGENTRY; +AssertCompileSize(RTSTRCACHEENTRY, 16); +/** Pointer to a big string cache entry. */ +typedef RTSTRCACHEBIGENTRY *PRTSTRCACHEBIGENTRY; +/** Pointer to a const big string cache entry. */ +typedef RTSTRCACHEBIGENTRY *PCRTSTRCACHEBIGENTRY; + + /** - * Allocation chunk. + * A free string cache entry. + */ +typedef struct RTSTRCACHEFREE +{ + /** Zero value indicating that it's a free entry (no refs, no hash). */ + uint32_t uZero; + /** Number of free bytes. Only used for > 32 byte allocations. */ + uint32_t cbFree; + /** Pointer to the next free item. */ + struct RTSTRCACHEFREE *pNext; +} RTSTRCACHEFREE; +AssertCompileSize(RTSTRCACHEENTRY, 16); +AssertCompileMembersAtSameOffset(RTSTRCACHEENTRY, cRefs, RTSTRCACHEFREE, uZero); +AssertCompileMembersAtSameOffset(RTSTRCACHEENTRY, szString, RTSTRCACHEFREE, pNext); +/** Pointer to a free string cache entry. */ +typedef RTSTRCACHEFREE *PRTSTRCACHEFREE; + +#ifdef RTSTRCACHE_WITH_MERGED_ALLOCATOR + +/** + * A free string cache entry with merging. + * + * This differs from RTSTRCACHEFREE only in having a back pointer for more + * efficient list management (doubly vs. singly linked lists). + */ +typedef struct RTSTRCACHEFREEMERGE +{ + /** Marker that indicates what kind of entry this is, either . */ + uint32_t uMarker; + /** Number of free bytes. Only used for > 32 byte allocations. */ + uint32_t cbFree; + /** Pointer to the main node. NULL for main nodes. */ + struct RTSTRCACHEFREEMERGE *pMain; + /** The free list entry. */ + RTLISTNODE ListEntry; + /** Pads the size up to the minimum allocation unit for the merge list. + * This both defines the minimum allocation unit and simplifies pointer + * manipulation during merging and splitting. */ + uint8_t abPadding[ARCH_BITS == 32 ? 44 : 32]; +} RTSTRCACHEFREEMERGE; +AssertCompileSize(RTSTRCACHEFREEMERGE, RT_BIT_32(RTSTRCACHE_MERGED_THRESHOLD_BIT)); +/** Pointer to a free cache string in the merge list. */ +typedef RTSTRCACHEFREEMERGE *PRTSTRCACHEFREEMERGE; + +/** RTSTRCACHEFREEMERGE::uMarker value indicating that it's the real free chunk + * header. Must be something that's invalid UTF-8 for both little and big + * endian system. */ +# define RTSTRCACHEFREEMERGE_MAIN UINT32_C(0xfffffff1) +/** RTSTRCACHEFREEMERGE::uMarker value indicating that it's part of a larger + * chunk of free memory. Must be something that's invalid UTF-8 for both little + * and big endian system. */ +# define RTSTRCACHEFREEMERGE_PART UINT32_C(0xfffffff2) + +#endif /* RTSTRCACHE_WITH_MERGED_ALLOCATOR */ + +/** + * Tracking structure chunk of memory used by the 16 byte or 32 byte + * allocations. + * + * This occupies the first entry in the chunk. */ typedef struct RTSTRCACHECHUNK { - /** Pointer to the main string cache structure. */ - struct RTSTRCACHEINT *pCache; - /** Padding to align the entries on a 32-byte boundary. */ - uint32_t au32Padding[8 - (ARCH_BITS == 64) - 4]; - /** The index of the first unused entry. */ - uint32_t iUnused; - /** The number of used entries. */ - uint32_t cUsed; - /** The number of entries in this chunk. */ - uint32_t cEntries; - /** The string cache entries, variable size. */ - RTSTRCACHEENTRY aEntries[1]; + /** The size of the chunk. */ + size_t cb; + /** Pointer to the next chunk. */ + struct RTSTRCACHECHUNK *pNext; } RTSTRCACHECHUNK; +AssertCompile(sizeof(RTSTRCACHECHUNK) <= sizeof(RTSTRCACHEENTRY)); +/** Pointer to the chunk tracking structure. */ +typedef RTSTRCACHECHUNK *PRTSTRCACHECHUNK; +/** + * Cache instance data. + */ typedef struct RTSTRCACHEINT { /** The string cache magic (RTSTRCACHE_MAGIC). */ - uint32_t u32Magic; + uint32_t u32Magic; + /** Ref counter for the cache handle. */ + uint32_t volatile cRefs; + /** The number of strings currently entered in the cache. */ + uint32_t cStrings; + /** The size of the hash table. */ + uint32_t cHashTab; + /** Pointer to the hash table. */ + PRTSTRCACHEENTRY *papHashTab; + /** Free list for allocations of the sizes defined by g_acbFixedLists. */ + PRTSTRCACHEFREE apFreeLists[RTSTRCACHE_NUM_OF_FIXED_SIZES]; +#ifdef RTSTRCACHE_WITH_MERGED_ALLOCATOR + /** Free lists based on */ + RTLISTANCHOR aMergedFreeLists[RTSTRCACHE_HEAP_THRESHOLD_BIT - RTSTRCACHE_MERGED_THRESHOLD_BIT + 1]; +#endif + /** List of allocated memory chunks. */ + PRTSTRCACHECHUNK pChunkList; + /** List of big cache entries. */ + RTLISTANCHOR BigEntryList; -} RTSTRCACHEINT; + /** @name Statistics + * @{ */ + /** The total size of all chunks. */ + size_t cbChunks; + /** The total length of all the strings, terminators included. */ + size_t cbStrings; + /** The total size of all the big entries. */ + size_t cbBigEntries; + /** Hash collisions. */ + uint32_t cHashCollisions; + /** Secondary hash collisions. */ + uint32_t cHashCollisions2; + /** The number of inserts to compare cHashCollisions to. */ + uint32_t cHashInserts; + /** The number of rehashes. */ + uint32_t cRehashes; + /** @} */ + /** Critical section protecting the cache structures. */ + RTCRITSECT CritSect; +} RTSTRCACHEINT; +/** Pointer to a cache instance. */ +typedef RTSTRCACHEINT *PRTSTRCACHEINT; /******************************************************************************* -* Defined Constants And Macros * +* Global Variables * *******************************************************************************/ -/** Validates a string cache handle, translating RTSTRCACHE_DEFAULT when found, - * and returns rc if not valid. */ -#define RTSTRCACHE_VALID_RETURN_RC(pStrCache, rc) \ - do { \ - if ((pStrCache) == RTMEMPOOL_DEFAULT) \ - (pStrCache) = &g_rtMemPoolDefault; \ - else \ - { \ - AssertPtrReturn((pStrCache), (rc)); \ - AssertReturn((pStrCache)->u32Magic == RTSTRCACHE_MAGIC, (rc)); \ - } \ - } while (0) +/** The entry sizes of the fixed lists (RTSTRCACHEINT::apFreeLists). */ +static const uint32_t g_acbFixedLists[RTSTRCACHE_NUM_OF_FIXED_SIZES] = +{ + 16, 32, 48, 64, 96, 128, 192, 256, 320, 384, 448, 512 +}; -/** Validates a memory pool entry and returns rc if not valid. */ -#define RTSTRCACHE_VALID_ENTRY_RETURN_RC(pEntry, rc) \ - do { \ - AssertPtrReturn(pEntry, (rc)); \ - AssertPtrNullReturn((pEntry)->pMemPool, (rc)); \ - Assert((pEntry)->cRefs < UINT32_MAX / 2); \ - AssertReturn((pEntry)->pMemPool->u32Magic == RTMEMPOOL_MAGIC, (rc)); \ - } while (0) +/** Init once for the default string cache. */ +static RTONCE g_rtStrCacheOnce = RTONCE_INITIALIZER; +/** The default string cache. */ +static RTSTRCACHE g_hrtStrCacheDefault = NIL_RTSTRCACHE; -/******************************************************************************* -* Global Variables * -*******************************************************************************/ - +/** @callback_method_impl{FNRTONCE, Initializes g_hrtStrCacheDefault} */ +static DECLCALLBACK(int) rtStrCacheInitDefault(void *pvUser) +{ + NOREF(pvUser); + return RTStrCacheCreate(&g_hrtStrCacheDefault, "Default"); +} RTDECL(int) RTStrCacheCreate(PRTSTRCACHE phStrCache, const char *pszName) { - AssertCompile(sizeof(RTSTRCACHE) == sizeof(RTMEMPOOL)); - AssertCompile(NIL_RTSTRCACHE == (RTSTRCACHE)NIL_RTMEMPOOL); - AssertCompile(RTSTRCACHE_DEFAULT == (RTSTRCACHE)RTMEMPOOL_DEFAULT); - return RTMemPoolCreate((PRTMEMPOOL)phStrCache, pszName); + int rc = VERR_NO_MEMORY; + PRTSTRCACHEINT pThis = (PRTSTRCACHEINT)RTMemAllocZ(sizeof(*pThis)); + if (pThis) + { + pThis->cHashTab = RTSTRCACHE_INITIAL_HASH_SIZE; + pThis->papHashTab = (PRTSTRCACHEENTRY*)RTMemAllocZ(sizeof(pThis->papHashTab[0]) * pThis->cHashTab); + if (pThis->papHashTab) + { + rc = RTCritSectInit(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + RTListInit(&pThis->BigEntryList); +#ifdef RTSTRCACHE_WITH_MERGED_ALLOCATOR + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aMergedFreeLists); i++) + RTListInit(&pThis->aMergedFreeLists[i]); +#endif + pThis->cRefs = 1; + pThis->u32Magic = RTSTRCACHE_MAGIC; + + *phStrCache = pThis; + return VINF_SUCCESS; + } + RTMemFree(pThis->papHashTab); + } + RTMemFree(pThis); + } + return rc; } RT_EXPORT_SYMBOL(RTStrCacheCreate); RTDECL(int) RTStrCacheDestroy(RTSTRCACHE hStrCache) { - if ( hStrCache == NIL_RTSTRCACHE - || hStrCache == RTSTRCACHE_DEFAULT) + if ( hStrCache == NIL_RTSTRCACHE + || hStrCache == RTSTRCACHE_DEFAULT) return VINF_SUCCESS; - return RTMemPoolDestroy((RTMEMPOOL)hStrCache); + + PRTSTRCACHEINT pThis = hStrCache; + RTSTRCACHE_VALID_RETURN_RC(pThis, VERR_INVALID_HANDLE); + + /* + * Invalidate it. Enter the crit sect just to be on the safe side. + */ + AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSTRCACHE_MAGIC_DEAD, RTSTRCACHE_MAGIC), VERR_INVALID_HANDLE); + RTCritSectEnter(&pThis->CritSect); + Assert(pThis->cRefs == 1); + + PRTSTRCACHECHUNK pChunk; + while ((pChunk = pThis->pChunkList) != NULL) + { + pThis->pChunkList = pChunk->pNext; + RTMemPageFree(pChunk, pChunk->cb); + } + + RTMemFree(pThis->papHashTab); + pThis->papHashTab = NULL; + pThis->cHashTab = 0; + + PRTSTRCACHEBIGENTRY pCur, pNext; + RTListForEachSafe(&pThis->BigEntryList, pCur, pNext, RTSTRCACHEBIGENTRY, ListEntry) + { + RTMemFree(pCur); + } + + RTCritSectLeave(&pThis->CritSect); + RTCritSectDelete(&pThis->CritSect); + + RTMemFree(pThis); + return VINF_SUCCESS; } RT_EXPORT_SYMBOL(RTStrCacheDestroy); +/** + * Selects the fixed free list index for a given minimum entry size. + * + * @returns Free list index. + * @param cbMin Minimum entry size. + */ +DECLINLINE(uint32_t) rtStrCacheSelectFixedList(uint32_t cbMin) +{ + Assert(cbMin <= g_acbFixedLists[RT_ELEMENTS(g_acbFixedLists) - 1]); + unsigned i = 0; + while (cbMin > g_acbFixedLists[i]) + i++; + return i; +} + + +#ifdef RT_STRICT +# define RTSTRCACHE_CHECK(a_pThis) do { rtStrCacheCheck(pThis); } while (0) +/** + * Internal cache check. + */ +static void rtStrCacheCheck(PRTSTRCACHEINT pThis) +{ +# ifdef RTSTRCACHE_WITH_MERGED_ALLOCATOR + for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aMergedFreeLists); i++) + { + PRTSTRCACHEFREEMERGE pFree; + RTListForEach(&pThis->aMergedFreeLists[i], pFree, RTSTRCACHEFREEMERGE, ListEntry) + { + Assert(pFree->uMarker == RTSTRCACHEFREEMERGE_MAIN); + Assert(pFree->cbFree > 0); + Assert(RT_ALIGN_32(pFree->cbFree, sizeof(*pFree)) == pFree->cbFree); + } + } +# endif +} +#else +# define RTSTRCACHE_CHECK(a_pThis) do { } while (0) +#endif + + +/** + * Finds the first empty hash table entry given a hash+length value. + * + * ASSUMES that the hash table isn't full. + * + * @returns Hash table index. + * @param pThis The string cache instance. + * @param uHashLen The hash + length (not RTSTRCACHEENTRY_BIG_LEN). + */ +static uint32_t rtStrCacheFindEmptyHashTabEntry(PRTSTRCACHEINT pThis, uint32_t uHashLen) +{ + uint32_t iHash = uHashLen % pThis->cHashTab; + for (;;) + { + PRTSTRCACHEENTRY pEntry = pThis->papHashTab[iHash]; + if (pEntry == NULL || pEntry == PRTSTRCACHEENTRY_NIL) + return iHash; + + /* Advance. */ + iHash += RTSTRCACHE_COLLISION_INCR(uHashLen); + iHash %= pThis->cHashTab; + } +} + +/** + * Grows the hash table. + * + * @returns vINF_SUCCESS or VERR_NO_MEMORY. + * @param pThis The string cache instance. + */ +static int rtStrCacheGrowHashTab(PRTSTRCACHEINT pThis) +{ + /* + * Allocate a new hash table two times the size of the old one. + */ + uint32_t cNew = pThis->cHashTab * RTSTRCACHE_HASH_GROW_FACTOR; + PRTSTRCACHEENTRY *papNew = (PRTSTRCACHEENTRY *)RTMemAllocZ(sizeof(papNew[0]) * cNew); + if (papNew == NULL) + return VERR_NO_MEMORY; + + /* + * Install the new table and move the items from the old table and into the new one. + */ + PRTSTRCACHEENTRY *papOld = pThis->papHashTab; + uint32_t iOld = pThis->cHashTab; + + pThis->papHashTab = papNew; + pThis->cHashTab = cNew; + pThis->cRehashes++; + + while (iOld-- > 0) + { + PRTSTRCACHEENTRY pEntry = papOld[iOld]; + if (pEntry != NULL && pEntry != PRTSTRCACHEENTRY_NIL) + { + uint32_t cchString = pEntry->cchString; + if (cchString == RTSTRCACHEENTRY_BIG_LEN) + cchString = RT_FROM_MEMBER(pEntry, RTSTRCACHEBIGENTRY, Core)->cchString; + + uint32_t iHash = rtStrCacheFindEmptyHashTabEntry(pThis, RT_MAKE_U32(pEntry->uHash, cchString)); + pThis->papHashTab[iHash] = pEntry; + } + } + + /* + * Free the old hash table. + */ + RTMemFree(papOld); + return VINF_SUCCESS; +} + +#ifdef RTSTRCACHE_WITH_MERGED_ALLOCATOR + +/** + * Link/Relink into the free right list. + * + * @param pThis The string cache instance. + * @param pFree The free string entry. + */ +static void rtStrCacheRelinkMerged(PRTSTRCACHEINT pThis, PRTSTRCACHEFREEMERGE pFree) +{ + Assert(pFree->uMarker == RTSTRCACHEFREEMERGE_MAIN); + Assert(pFree->cbFree > 0); + Assert(RT_ALIGN_32(pFree->cbFree, sizeof(*pFree)) == pFree->cbFree); + + if (!RTListIsEmpty(&pFree->ListEntry)) + RTListNodeRemove(&pFree->ListEntry); + + uint32_t iList = (ASMBitLastSetU32(pFree->cbFree) - 1) - RTSTRCACHE_MERGED_THRESHOLD_BIT; + if (iList >= RT_ELEMENTS(pThis->aMergedFreeLists)) + iList = RT_ELEMENTS(pThis->aMergedFreeLists) - 1; + + RTListPrepend(&pThis->aMergedFreeLists[iList], &pFree->ListEntry); +} + + +/** + * Allocate a cache entry from the merged free lists. + * + * @returns Pointer to the cache entry on success, NULL on allocation error. + * @param pThis The string cache instance. + * @param uHash The full hash of the string. + * @param pchString The string. + * @param cchString The string length. + * @param cbEntry The required entry size. + */ +static PRTSTRCACHEENTRY rtStrCacheAllocMergedEntry(PRTSTRCACHEINT pThis, uint32_t uHash, + const char *pchString, uint32_t cchString, uint32_t cbEntry) +{ + cbEntry = RT_ALIGN_32(cbEntry, sizeof(RTSTRCACHEFREEMERGE)); + Assert(cbEntry > cchString); + + /* + * Search the list heads first. + */ + PRTSTRCACHEFREEMERGE pFree = NULL; + + uint32_t iList = ASMBitLastSetU32(cbEntry) - 1; + if (!RT_IS_POWER_OF_TWO(cbEntry)) + iList++; + iList -= RTSTRCACHE_MERGED_THRESHOLD_BIT; + + while (iList < RT_ELEMENTS(pThis->aMergedFreeLists)) + { + pFree = RTListGetFirst(&pThis->aMergedFreeLists[iList], RTSTRCACHEFREEMERGE, ListEntry); + if (pFree) + { + /* + * Found something. Should we we split it? We split from the end + * to avoid having to update all the sub entries. + */ + Assert(pFree->uMarker == RTSTRCACHEFREEMERGE_MAIN); + Assert(pFree->cbFree >= cbEntry); + Assert(RT_ALIGN_32(pFree->cbFree, sizeof(*pFree)) == pFree->cbFree); + + if (pFree->cbFree == cbEntry) + RTListNodeRemove(&pFree->ListEntry); + else + { + uint32_t cRemainder = (pFree->cbFree - cbEntry) / sizeof(*pFree); + PRTSTRCACHEFREEMERGE pRemainder = pFree; + pFree += cRemainder; + + Assert((pRemainder->cbFree - cbEntry) == cRemainder * sizeof(*pFree)); + pRemainder->cbFree = cRemainder * sizeof(*pFree); + + rtStrCacheRelinkMerged(pThis, pRemainder); + } + break; + } + iList++; + } + if (!pFree) + { + /* + * Allocate a new block. (We could search the list below in some + * cases, but it's too much effort to write and execute). + */ + size_t const cbChunk = RTSTRCACHE_MERGED_GROW_SIZE; AssertReturn(cbChunk > cbEntry * 2, NULL); + PRTSTRCACHECHUNK pChunk = (PRTSTRCACHECHUNK)RTMemPageAlloc(cbChunk); + if (!pChunk) + return NULL; + pChunk->cb = cbChunk; + pChunk->pNext = pThis->pChunkList; + pThis->pChunkList = pChunk; + pThis->cbChunks += cbChunk; + AssertCompile(sizeof(*pChunk) <= sizeof(*pFree)); + + /* + * Get one node for the allocation at hand. + */ + pFree = (PRTSTRCACHEFREEMERGE)((uintptr_t)pChunk + sizeof(*pFree)); + + /* + * Create a free block out of the remainder (always a reminder). + */ + PRTSTRCACHEFREEMERGE pNewFree = (PRTSTRCACHEFREEMERGE)((uintptr_t)pFree + cbEntry); + pNewFree->uMarker = RTSTRCACHEFREEMERGE_MAIN; + pNewFree->cbFree = cbChunk - sizeof(*pNewFree) - cbEntry; Assert(pNewFree->cbFree < cbChunk && pNewFree->cbFree > 0); + pNewFree->pMain = NULL; + RTListInit(&pNewFree->ListEntry); + + uint32_t iInternalBlock = pNewFree->cbFree / sizeof(*pNewFree); + while (iInternalBlock-- > 1) + { + pNewFree[iInternalBlock].uMarker = RTSTRCACHEFREEMERGE_PART; + pNewFree[iInternalBlock].cbFree = 0; + pNewFree[iInternalBlock].pMain = pNewFree; + } + + rtStrCacheRelinkMerged(pThis, pNewFree); + } + + /* + * Initialize the entry. We zero all bytes we don't use so they cannot + * accidentally be mistaken for a free entry. + */ + ASMCompilerBarrier(); + PRTSTRCACHEENTRY pEntry = (PRTSTRCACHEENTRY)pFree; + pEntry->cRefs = 1; + pEntry->uHash = (uint16_t)uHash; + pEntry->cchString = (uint16_t)cchString; + memcpy(pEntry->szString, pchString, cchString); + RT_BZERO(&pEntry->szString[cchString], cbEntry - RT_UOFFSETOF(RTSTRCACHEENTRY, szString) - cchString); + + RTSTRCACHE_CHECK(pThis); + + return pEntry; +} + +#endif /* RTSTRCACHE_WITH_MERGED_ALLOCATOR */ + +/** + * Allocate a cache entry from the heap. + * + * @returns Pointer to the cache entry on success, NULL on allocation error. + * @param pThis The string cache instance. + * @param uHash The full hash of the string. + * @param pchString The string. + * @param cchString The string length. + */ +static PRTSTRCACHEENTRY rtStrCacheAllocHeapEntry(PRTSTRCACHEINT pThis, uint32_t uHash, + const char *pchString, uint32_t cchString) +{ + /* + * Allocate a heap block for storing the string. We do some size aligning + * here to encourage the heap to give us optimal alignment. + */ + size_t cbEntry = RT_UOFFSETOF(RTSTRCACHEBIGENTRY, Core.szString[cchString + 1]); + PRTSTRCACHEBIGENTRY pBigEntry = (PRTSTRCACHEBIGENTRY)RTMemAlloc(RT_ALIGN_Z(cbEntry, RTSTRCACHE_HEAP_ENTRY_SIZE_ALIGN)); + if (!pBigEntry) + return NULL; + + /* + * Initialize the block. + */ + RTListAppend(&pThis->BigEntryList, &pBigEntry->ListEntry); + pThis->cbBigEntries += cbEntry; + pBigEntry->cchString = cchString; + pBigEntry->uHash = uHash; + pBigEntry->Core.cRefs = 1; + pBigEntry->Core.uHash = (uint16_t)uHash; + pBigEntry->Core.cchString = RTSTRCACHEENTRY_BIG_LEN; + memcpy(pBigEntry->Core.szString, pchString, cchString); + pBigEntry->Core.szString[cchString] = '\0'; + + return &pBigEntry->Core; +} + + +/** + * Allocate a cache entry from a fixed size free list. + * + * @returns Pointer to the cache entry on success, NULL on allocation error. + * @param pThis The string cache instance. + * @param uHash The full hash of the string. + * @param pchString The string. + * @param cchString The string length. + * @param iFreeList Which free list. + */ +static PRTSTRCACHEENTRY rtStrCacheAllocFixedEntry(PRTSTRCACHEINT pThis, uint32_t uHash, + const char *pchString, uint32_t cchString, uint32_t iFreeList) +{ + /* + * Get an entry from the free list. If empty, allocate another chunk of + * memory and split it up into free entries of the desired size. + */ + PRTSTRCACHEFREE pFree = pThis->apFreeLists[iFreeList]; + if (!pFree) + { + PRTSTRCACHECHUNK pChunk = (PRTSTRCACHECHUNK)RTMemPageAlloc(RTSTRCACHE_FIXED_GROW_SIZE); + if (!pChunk) + return NULL; + pChunk->cb = RTSTRCACHE_FIXED_GROW_SIZE; + pChunk->pNext = pThis->pChunkList; + pThis->pChunkList = pChunk; + pThis->cbChunks += RTSTRCACHE_FIXED_GROW_SIZE; + + PRTSTRCACHEFREE pPrev = NULL; + uint32_t const cbEntry = g_acbFixedLists[iFreeList]; + uint32_t cLeft = RTSTRCACHE_FIXED_GROW_SIZE / cbEntry - 1; + pFree = (PRTSTRCACHEFREE)((uintptr_t)pChunk + cbEntry); + + Assert(sizeof(*pChunk) <= cbEntry); + Assert(sizeof(*pFree) <= cbEntry); + Assert(cbEntry < RTSTRCACHE_FIXED_GROW_SIZE / 16); + + while (cLeft-- > 0) + { + pFree->uZero = 0; + pFree->cbFree = cbEntry; + pFree->pNext = pPrev; + pPrev = pFree; + pFree = (PRTSTRCACHEFREE)((uintptr_t)pFree + cbEntry); + } + + Assert(pPrev); + pThis->apFreeLists[iFreeList] = pFree = pPrev; + } + + /* + * Unlink it. + */ + pThis->apFreeLists[iFreeList] = pFree->pNext; + ASMCompilerBarrier(); + + /* + * Initialize the entry. + */ + PRTSTRCACHEENTRY pEntry = (PRTSTRCACHEENTRY)pFree; + pEntry->cRefs = 1; + pEntry->uHash = (uint16_t)uHash; + pEntry->cchString = (uint16_t)cchString; + memcpy(pEntry->szString, pchString, cchString); + pEntry->szString[cchString] = '\0'; + + return pEntry; +} + + +/** + * Looks up a string in the hash table. + * + * @returns Pointer to the string cache entry, NULL + piFreeHashTabEntry if not + * found. + * @param pThis The string cache instance. + * @param uHashLen The hash + length (not RTSTRCACHEENTRY_BIG_LEN). + * @param cchString The real length. + * @param pchString The string. + * @param piFreeHashTabEntry Where to store the index insertion index if NULL + * is returned (same as what + * rtStrCacheFindEmptyHashTabEntry would return). + * @param pcCollisions Where to return a collision counter. + */ +static PRTSTRCACHEENTRY rtStrCacheLookUp(PRTSTRCACHEINT pThis, uint32_t uHashLen, uint32_t cchString, const char *pchString, + uint32_t *piFreeHashTabEntry, uint32_t *pcCollisions) +{ + *piFreeHashTabEntry = UINT32_MAX; + *pcCollisions = 0; + + uint16_t cchStringFirst = RT_UOFFSETOF(RTSTRCACHEENTRY, szString[cchString + 1]) < RTSTRCACHE_HEAP_THRESHOLD + ? (uint16_t)cchString : RTSTRCACHEENTRY_BIG_LEN; + uint32_t iHash = uHashLen % pThis->cHashTab; + for (;;) + { + PRTSTRCACHEENTRY pEntry = pThis->papHashTab[iHash]; + + /* Give up if NULL, but record the index for insertion. */ + if (pEntry == NULL) + { + if (*piFreeHashTabEntry == UINT32_MAX) + *piFreeHashTabEntry = iHash; + return NULL; + } + + if (pEntry != PRTSTRCACHEENTRY_NIL) + { + /* Compare. */ + if ( pEntry->uHash == (uint16_t)uHashLen + && pEntry->cchString == cchStringFirst) + { + if (pEntry->cchString != RTSTRCACHEENTRY_BIG_LEN) + { + if ( !memcmp(pEntry->szString, pchString, cchString) + && pEntry->szString[cchString] == '\0') + return pEntry; + } + else + { + PRTSTRCACHEBIGENTRY pBigEntry = RT_FROM_MEMBER(pEntry, RTSTRCACHEBIGENTRY, Core); + if ( pBigEntry->cchString == cchString + && !memcmp(pBigEntry->Core.szString, pchString, cchString)) + return &pBigEntry->Core; + } + } + + if (*piFreeHashTabEntry == UINT32_MAX) + *pcCollisions += 1; + } + /* Record the first NIL index for insertion in case we don't get a hit. */ + else if (*piFreeHashTabEntry == UINT32_MAX) + *piFreeHashTabEntry = iHash; + + /* Advance. */ + iHash += RTSTRCACHE_COLLISION_INCR(uHashLen); + iHash %= pThis->cHashTab; + } +} + + RTDECL(const char *) RTStrCacheEnterN(RTSTRCACHE hStrCache, const char *pchString, size_t cchString) { - AssertPtr(pchString); + PRTSTRCACHEINT pThis = hStrCache; + RTSTRCACHE_VALID_RETURN_RC(pThis, NULL); + + + /* + * Calculate the hash and figure the exact string length, then look for an existing entry. + */ + uint32_t const uHash = sdbmN(pchString, cchString, &cchString); + uint32_t const uHashLen = RT_MAKE_U32(uHash, cchString); AssertReturn(cchString < _1G, NULL); - Assert(!RTStrEnd(pchString, cchString)); + uint32_t const cchString32 = (uint32_t)cchString; + + RTCritSectEnter(&pThis->CritSect); + RTSTRCACHE_CHECK(pThis); - return (const char *)RTMemPoolDupEx((RTMEMPOOL)hStrCache, pchString, cchString, 1); + uint32_t cCollisions; + uint32_t iFreeHashTabEntry; + PRTSTRCACHEENTRY pEntry = rtStrCacheLookUp(pThis, uHashLen, cchString32, pchString, &iFreeHashTabEntry, &cCollisions); + if (pEntry) + { + uint32_t cRefs = ASMAtomicIncU32(&pEntry->cRefs); + Assert(cRefs < UINT32_MAX / 2); + } + else + { + /* + * Allocate a new entry. + */ + uint32_t cbEntry = cchString32 + 1U + RT_UOFFSETOF(RTSTRCACHEENTRY, szString); + if (cbEntry >= RTSTRCACHE_HEAP_THRESHOLD) + pEntry = rtStrCacheAllocHeapEntry(pThis, uHash, pchString, cchString32); +#ifdef RTSTRCACHE_WITH_MERGED_ALLOCATOR + else if (cbEntry >= RTSTRCACHE_MERGED_THRESHOLD_BIT) + pEntry = rtStrCacheAllocMergedEntry(pThis, uHash, pchString, cchString32, cbEntry); +#endif + else + pEntry = rtStrCacheAllocFixedEntry(pThis, uHash, pchString, cchString32, + rtStrCacheSelectFixedList(cbEntry)); + if (!pEntry) + { + RTSTRCACHE_CHECK(pThis); + RTCritSectLeave(&pThis->CritSect); + return NULL; + } + + /* + * Insert it into the hash table. + */ + if (pThis->cHashTab - pThis->cStrings < pThis->cHashTab / 2) + { + int rc = rtStrCacheGrowHashTab(pThis); + if (RT_SUCCESS(rc)) + iFreeHashTabEntry = rtStrCacheFindEmptyHashTabEntry(pThis, uHashLen); + else if (pThis->cHashTab - pThis->cStrings <= pThis->cHashTab / 8) /* 12.5% full => error */ + { + pThis->papHashTab[iFreeHashTabEntry] = pEntry; + pThis->cStrings++; + pThis->cHashInserts++; + pThis->cHashCollisions += cCollisions > 0; + pThis->cHashCollisions2 += cCollisions > 1; + pThis->cbStrings += cchString32 + 1; + RTStrCacheRelease(hStrCache, pEntry->szString); + + RTSTRCACHE_CHECK(pThis); + RTCritSectLeave(&pThis->CritSect); + return NULL; + } + } + + pThis->papHashTab[iFreeHashTabEntry] = pEntry; + pThis->cStrings++; + pThis->cHashInserts++; + pThis->cHashCollisions += cCollisions > 0; + pThis->cHashCollisions2 += cCollisions > 1; + pThis->cbStrings += cchString32 + 1; + Assert(pThis->cStrings < pThis->cHashTab && pThis->cStrings > 0); + } + + RTSTRCACHE_CHECK(pThis); + RTCritSectLeave(&pThis->CritSect); + return pEntry->szString; } RT_EXPORT_SYMBOL(RTStrCacheEnterN); @@ -166,19 +920,249 @@ RTDECL(const char *) RTStrCacheEnter(RTSTRCACHE hStrCache, const char *psz) RT_EXPORT_SYMBOL(RTStrCacheEnter); +static const char *rtStrCacheEnterLowerWorker(PRTSTRCACHEINT pThis, const char *pchString, size_t cchString) +{ + /* + * Try use a dynamic heap buffer first. + */ + if (cchString < 512) + { + char *pszStackBuf = (char *)alloca(cchString + 1); + if (pszStackBuf) + { + memcpy(pszStackBuf, pchString, cchString); + pszStackBuf[cchString] = '\0'; + RTStrToLower(pszStackBuf); + return RTStrCacheEnterN(pThis, pszStackBuf, cchString); + } + } + + /* + * Fall back on heap. + */ + char *pszHeapBuf = (char *)RTMemTmpAlloc(cchString + 1); + if (!pszHeapBuf) + return NULL; + memcpy(pszHeapBuf, pchString, cchString); + pszHeapBuf[cchString] = '\0'; + RTStrToLower(pszHeapBuf); + const char *pszRet = RTStrCacheEnterN(pThis, pszHeapBuf, cchString); + RTMemTmpFree(pszHeapBuf); + return pszRet; +} + +RTDECL(const char *) RTStrCacheEnterLowerN(RTSTRCACHE hStrCache, const char *pchString, size_t cchString) +{ + PRTSTRCACHEINT pThis = hStrCache; + RTSTRCACHE_VALID_RETURN_RC(pThis, NULL); + return rtStrCacheEnterLowerWorker(pThis, pchString, RTStrNLen(pchString, cchString)); +} +RT_EXPORT_SYMBOL(RTStrCacheEnterLowerN); + + +RTDECL(const char *) RTStrCacheEnterLower(RTSTRCACHE hStrCache, const char *psz) +{ + PRTSTRCACHEINT pThis = hStrCache; + RTSTRCACHE_VALID_RETURN_RC(pThis, NULL); + return rtStrCacheEnterLowerWorker(pThis, psz, strlen(psz)); +} +RT_EXPORT_SYMBOL(RTStrCacheEnterLower); + + RTDECL(uint32_t) RTStrCacheRetain(const char *psz) { AssertPtr(psz); - return RTMemPoolRetain((void *)psz); + + PRTSTRCACHEENTRY pStr = RT_FROM_MEMBER(psz, RTSTRCACHEENTRY, szString); + Assert(!((uintptr_t)pStr & 15) || pStr->cchString == RTSTRCACHEENTRY_BIG_LEN); + + uint32_t cRefs = ASMAtomicIncU32(&pStr->cRefs); + Assert(cRefs > 1); + Assert(cRefs < UINT32_MAX / 2); + + return cRefs; } RT_EXPORT_SYMBOL(RTStrCacheRetain); +static uint32_t rtStrCacheFreeEntry(PRTSTRCACHEINT pThis, PRTSTRCACHEENTRY pStr) +{ + RTCritSectEnter(&pThis->CritSect); + RTSTRCACHE_CHECK(pThis); + + /* Remove it from the hash table. */ + uint32_t cchString = pStr->cchString == RTSTRCACHEENTRY_BIG_LEN + ? RT_FROM_MEMBER(pStr, RTSTRCACHEBIGENTRY, Core)->cchString + : pStr->cchString; + uint32_t uHashLen = RT_MAKE_U32(pStr->uHash, cchString); + uint32_t iHash = uHashLen % pThis->cHashTab; + if (pThis->papHashTab[iHash] == pStr) + pThis->papHashTab[iHash] = PRTSTRCACHEENTRY_NIL; + else + { + do + { + AssertBreak(pThis->papHashTab[iHash] != NULL); + iHash += RTSTRCACHE_COLLISION_INCR(uHashLen); + iHash %= pThis->cHashTab; + } while (pThis->papHashTab[iHash] != pStr); + if (RT_LIKELY(pThis->papHashTab[iHash] == pStr)) + pThis->papHashTab[iHash] = PRTSTRCACHEENTRY_NIL; + else + { + AssertFailed(); + iHash = pThis->cHashTab; + while (iHash-- > 0) + if (pThis->papHashTab[iHash] == pStr) + break; + AssertMsgFailed(("iHash=%u cHashTab=%u\n", iHash, pThis->cHashTab)); + } + } + + pThis->cStrings--; + pThis->cbStrings -= cchString; + Assert(pThis->cStrings < pThis->cHashTab); + + /* Free it. */ + if (pStr->cchString != RTSTRCACHEENTRY_BIG_LEN) + { + uint32_t const cbMin = pStr->cchString + 1U + RT_UOFFSETOF(RTSTRCACHEENTRY, szString); +#ifdef RTSTRCACHE_WITH_MERGED_ALLOCATOR + if (cbMin <= RTSTRCACHE_MAX_FIXED) +#endif + { + /* + * No merging, just add it to the list. + */ + uint32_t const iFreeList = rtStrCacheSelectFixedList(cbMin); + ASMCompilerBarrier(); + PRTSTRCACHEFREE pFreeStr = (PRTSTRCACHEFREE)pStr; + pFreeStr->cbFree = cbMin; + pFreeStr->uZero = 0; + pFreeStr->pNext = pThis->apFreeLists[iFreeList]; + pThis->apFreeLists[iFreeList] = pFreeStr; + } +#ifdef RTSTRCACHE_WITH_MERGED_ALLOCATOR + else + { + /* + * Complicated mode, we merge with adjecent nodes. + */ + ASMCompilerBarrier(); + PRTSTRCACHEFREEMERGE pFreeStr = (PRTSTRCACHEFREEMERGE)pStr; + pFreeStr->cbFree = RT_ALIGN_32(cbMin, sizeof(*pFreeStr)); + pFreeStr->uMarker = RTSTRCACHEFREEMERGE_MAIN; + pFreeStr->pMain = NULL; + RTListInit(&pFreeStr->ListEntry); + + /* + * Merge with previous? + * (Reading one block back is safe because there is always the + * RTSTRCACHECHUNK structure at the head of each memory chunk.) + */ + uint32_t cInternalBlocks = pFreeStr->cbFree / sizeof(*pFreeStr); + PRTSTRCACHEFREEMERGE pMain = pFreeStr - 1; + if ( pMain->uMarker == RTSTRCACHEFREEMERGE_MAIN + || pMain->uMarker == RTSTRCACHEFREEMERGE_PART) + { + while (pMain->uMarker != RTSTRCACHEFREEMERGE_MAIN) + pMain--; + pMain->cbFree += pFreeStr->cbFree; + } + else + { + pMain = pFreeStr; + pFreeStr++; + cInternalBlocks--; + } + + /* + * Mark internal blocks in the string we're freeing. + */ + while (cInternalBlocks-- > 0) + { + pFreeStr->uMarker = RTSTRCACHEFREEMERGE_PART; + pFreeStr->cbFree = 0; + pFreeStr->pMain = pMain; + RTListInit(&pFreeStr->ListEntry); + pFreeStr++; + } + + /* + * Merge with next? Limitation: We won't try cross page boundraries. + * (pFreeStr points to the next first free enter after the string now.) + */ + if ( PAGE_ADDRESS(pFreeStr) == PAGE_ADDRESS(&pFreeStr[-1]) + && pFreeStr->uMarker == RTSTRCACHEFREEMERGE_MAIN) + { + pMain->cbFree += pFreeStr->cbFree; + cInternalBlocks = pFreeStr->cbFree / sizeof(*pFreeStr); + Assert(cInternalBlocks > 0); + + /* Update the main block we merge with. */ + pFreeStr->cbFree = 0; + pFreeStr->uMarker = RTSTRCACHEFREEMERGE_PART; + RTListNodeRemove(&pFreeStr->ListEntry); + RTListInit(&pFreeStr->ListEntry); + + /* Change the internal blocks we merged in. */ + cInternalBlocks--; + while (cInternalBlocks-- > 0) + { + pFreeStr++; + pFreeStr->pMain = pMain; + Assert(pFreeStr->uMarker == RTSTRCACHEFREEMERGE_PART); + Assert(!pFreeStr->cbFree); + } + } + + /* + * Add/relink into the appropriate free list. + */ + rtStrCacheRelinkMerged(pThis, pMain); + } +#endif /* RTSTRCACHE_WITH_MERGED_ALLOCATOR */ + RTSTRCACHE_CHECK(pThis); + RTCritSectLeave(&pThis->CritSect); + } + else + { + /* Big string. */ + PRTSTRCACHEBIGENTRY pBigStr = RT_FROM_MEMBER(pStr, RTSTRCACHEBIGENTRY, Core); + RTListNodeRemove(&pBigStr->ListEntry); + pThis->cbBigEntries -= RT_ALIGN_32(RT_UOFFSETOF(RTSTRCACHEBIGENTRY, Core.szString[cchString + 1]), + RTSTRCACHE_HEAP_ENTRY_SIZE_ALIGN); + + RTSTRCACHE_CHECK(pThis); + RTCritSectLeave(&pThis->CritSect); + + RTMemFree(pBigStr); + } + + return 0; +} + RTDECL(uint32_t) RTStrCacheRelease(RTSTRCACHE hStrCache, const char *psz) { if (!psz) return 0; - return RTMemPoolRelease((RTMEMPOOL)hStrCache, (void *)psz); + + PRTSTRCACHEINT pThis = hStrCache; + RTSTRCACHE_VALID_RETURN_RC(pThis, UINT32_MAX); + + AssertPtr(psz); + PRTSTRCACHEENTRY pStr = RT_FROM_MEMBER(psz, RTSTRCACHEENTRY, szString); + Assert(!((uintptr_t)pStr & 15) || pStr->cchString == RTSTRCACHEENTRY_BIG_LEN); + + /* + * Drop a reference and maybe free the entry. + */ + uint32_t cRefs = ASMAtomicDecU32(&pStr->cRefs); + Assert(cRefs < UINT32_MAX / 2); + if (!cRefs) + return rtStrCacheFreeEntry(pThis, pStr); + + return cRefs; } RT_EXPORT_SYMBOL(RTStrCacheRelease); @@ -187,7 +1171,54 @@ RTDECL(size_t) RTStrCacheLength(const char *psz) { if (!psz) return 0; - return strlen(psz); + + AssertPtr(psz); + PRTSTRCACHEENTRY pStr = RT_FROM_MEMBER(psz, RTSTRCACHEENTRY, szString); + if (pStr->cchString == RTSTRCACHEENTRY_BIG_LEN) + { + PRTSTRCACHEBIGENTRY pBigStr = RT_FROM_MEMBER(psz, RTSTRCACHEBIGENTRY, Core.szString); + return pBigStr->cchString; + } + Assert(!((uintptr_t)pStr & 15)); + return pStr->cchString; } RT_EXPORT_SYMBOL(RTStrCacheLength); + +RTDECL(bool) RTStrCacheIsRealImpl(void) +{ + return true; +} +RT_EXPORT_SYMBOL(RTStrCacheIsRealImpl); + + +RTDECL(uint32_t) RTStrCacheGetStats(RTSTRCACHE hStrCache, size_t *pcbStrings, size_t *pcbChunks, size_t *pcbBigEntries, + uint32_t *pcHashCollisions, uint32_t *pcHashCollisions2, uint32_t *pcHashInserts, + uint32_t *pcRehashes) +{ + PRTSTRCACHEINT pThis = hStrCache; + RTSTRCACHE_VALID_RETURN_RC(pThis, UINT32_MAX); + + RTCritSectEnter(&pThis->CritSect); + + if (pcbStrings) + *pcbStrings = pThis->cbStrings; + if (pcbChunks) + *pcbChunks = pThis->cbChunks; + if (pcbBigEntries) + *pcbBigEntries = pThis->cbBigEntries; + if (pcHashCollisions) + *pcHashCollisions = pThis->cHashCollisions; + if (pcHashCollisions2) + *pcHashCollisions2 = pThis->cHashCollisions2; + if (pcHashInserts) + *pcHashInserts = pThis->cHashInserts; + if (pcRehashes) + *pcRehashes = pThis->cRehashes; + uint32_t cStrings = pThis->cStrings; + + RTCritSectLeave(&pThis->CritSect); + return cStrings; +} +RT_EXPORT_SYMBOL(RTStrCacheRelease); + diff --git a/src/VBox/Runtime/common/string/strchr.asm b/src/VBox/Runtime/common/string/strchr.asm index 7163f28c..e890e9c8 100644 --- a/src/VBox/Runtime/common/string/strchr.asm +++ b/src/VBox/Runtime/common/string/strchr.asm @@ -4,7 +4,7 @@ ; ; -; Copyright (C) 2006-2007 Oracle Corporation +; Copyright (C) 2006-2010 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strchr_alias.c b/src/VBox/Runtime/common/string/strchr_alias.c index 4b4fe4b1..63a9a598 100644 --- a/src/VBox/Runtime/common/string/strchr_alias.c +++ b/src/VBox/Runtime/common/string/strchr_alias.c @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strcmp.asm b/src/VBox/Runtime/common/string/strcmp.asm index 574e5d14..31de02d8 100644 --- a/src/VBox/Runtime/common/string/strcmp.asm +++ b/src/VBox/Runtime/common/string/strcmp.asm @@ -4,7 +4,7 @@ ; ; -; Copyright (C) 2006-2007 Oracle Corporation +; Copyright (C) 2006-2010 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strcmp_alias.c b/src/VBox/Runtime/common/string/strcmp_alias.c index 3c5f7f83..9347aff3 100644 --- a/src/VBox/Runtime/common/string/strcmp_alias.c +++ b/src/VBox/Runtime/common/string/strcmp_alias.c @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strcpy.asm b/src/VBox/Runtime/common/string/strcpy.asm index bee8738b..85f7edec 100644 --- a/src/VBox/Runtime/common/string/strcpy.asm +++ b/src/VBox/Runtime/common/string/strcpy.asm @@ -4,7 +4,7 @@ ; ; -; Copyright (C) 2006-2007 Oracle Corporation +; Copyright (C) 2006-2010 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strcpy.cpp b/src/VBox/Runtime/common/string/strcpy.cpp index 55da775d..3ea6cf00 100644 --- a/src/VBox/Runtime/common/string/strcpy.cpp +++ b/src/VBox/Runtime/common/string/strcpy.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strcpy_alias.c b/src/VBox/Runtime/common/string/strcpy_alias.c index 517cd801..d703d4cd 100644 --- a/src/VBox/Runtime/common/string/strcpy_alias.c +++ b/src/VBox/Runtime/common/string/strcpy_alias.c @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strformat.cpp b/src/VBox/Runtime/common/string/strformat.cpp index 56dc5ec5..335d3033 100644 --- a/src/VBox/Runtime/common/string/strformat.cpp +++ b/src/VBox/Runtime/common/string/strformat.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strformatnum.cpp b/src/VBox/Runtime/common/string/strformatnum.cpp index f5b68740..777e8ec7 100644 --- a/src/VBox/Runtime/common/string/strformatnum.cpp +++ b/src/VBox/Runtime/common/string/strformatnum.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -49,7 +49,7 @@ RTDECL(ssize_t) RTStrFormatU8(char *pszBuf, size_t cbBuf, uint8_t u8Value, unsig { char szTmp[64]; cchRet = RTStrFormatNumber(szTmp, u8Value, uiBase, cchWidth, cchPrecision, fFlags); - if ((size_t)cchRet <= cbBuf) + if ((size_t)cchRet < cbBuf) memcpy(pszBuf, szTmp, cchRet + 1); else { diff --git a/src/VBox/Runtime/common/string/strformatrt.cpp b/src/VBox/Runtime/common/string/strformatrt.cpp index b5d23853..4dd69f17 100644 --- a/src/VBox/Runtime/common/string/strformatrt.cpp +++ b/src/VBox/Runtime/common/string/strformatrt.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -56,6 +56,121 @@ #include "internal/string.h" +/** + * Helper function to format IPv6 address according to RFC 5952. + * + * @returns The number of bytes formatted. + * @param pfnOutput Pointer to output function. + * @param pvArgOutput Argument for the output function. + * @param pIpv6Addr IPv6 address + */ +static size_t rtstrFormatIPv6(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, PCRTNETADDRIPV6 pIpv6Addr) +{ + size_t cch = 0; /* result */ + + bool fEmbeddedIpv4; + size_t cwHexPart; + size_t cwZeroRun, cwLongestZeroRun; + size_t iZeroStart, iLongestZeroStart; + size_t idx; + + Assert(pIpv6Addr != NULL); + + /* + * Check for embedded IPv4 address. + * + * IPv4-compatible - ::11.22.33.44 (obsolete) + * IPv4-mapped - ::ffff:11.22.33.44 + * IPv4-translated - ::ffff:0:11.22.33.44 (RFC 2765) + */ + fEmbeddedIpv4 = false; + cwHexPart = RT_ELEMENTS(pIpv6Addr->au16); + if (pIpv6Addr->au64[0] == 0 + && ( (pIpv6Addr->au32[2] == 0 + && ( pIpv6Addr->au32[3] != 0 + && pIpv6Addr->au32[3] != RT_H2BE_U32_C(1))) + || pIpv6Addr->au32[2] == RT_H2BE_U32_C(0x0000ffff) + || pIpv6Addr->au32[2] == RT_H2BE_U32_C(0xffff0000))) + { + fEmbeddedIpv4 = true; + cwHexPart -= 2; + } + + cwZeroRun = cwLongestZeroRun = 0; + iZeroStart = iLongestZeroStart = -1; + for (idx = 0; idx <= cwHexPart; ++idx) + { + if (idx < cwHexPart && pIpv6Addr->au16[idx] == 0) + { + if (cwZeroRun == 0) + { + cwZeroRun = 1; + iZeroStart = idx; + } + else + ++cwZeroRun; + } + else + { + if (cwZeroRun != 0) + { + if (cwZeroRun > 1 && cwZeroRun > cwLongestZeroRun) + { + cwLongestZeroRun = cwZeroRun; + iLongestZeroStart = iZeroStart; + } + cwZeroRun = 0; + iZeroStart = -1; + } + } + } + + if (cwLongestZeroRun == 0) + { + for (idx = 0; idx < cwHexPart; ++idx) + cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, + "%s%x", + idx == 0 ? "" : ":", + RT_BE2H_U16(pIpv6Addr->au16[idx])); + + if (fEmbeddedIpv4) + cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, ":"); + } + else + { + const size_t iLongestZeroEnd = iLongestZeroStart + cwLongestZeroRun; + + if (iLongestZeroStart == 0) + cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, ":"); + else + for (idx = 0; idx < iLongestZeroStart; ++idx) + cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, + "%x:", RT_BE2H_U16(pIpv6Addr->au16[idx])); + + if (iLongestZeroEnd == cwHexPart) + cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, ":"); + else + { + for (idx = iLongestZeroEnd; idx < cwHexPart; ++idx) + cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, + ":%x", RT_BE2H_U16(pIpv6Addr->au16[idx])); + + if (fEmbeddedIpv4) + cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, ":"); + } + } + + if (fEmbeddedIpv4) + cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, + "%u.%u.%u.%u", + pIpv6Addr->au8[12], + pIpv6Addr->au8[13], + pIpv6Addr->au8[14], + pIpv6Addr->au8[15]); + + return cch; +} + /** * Callback to format iprt formatting extentions. @@ -141,7 +256,7 @@ DECLHIDDEN(size_t) rtstrFormatRt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, co { STRMEM("Gx"), sizeof(RTGCUINT), 16, RTSF_INT, 0 }, { STRMEM("Hi"), sizeof(RTHCINT), 10, RTSF_INT, RTSTR_F_VALSIGNED }, { STRMEM("Hp"), sizeof(RTHCPHYS), 16, RTSF_INTW, 0 }, - { STRMEM("Hr"), sizeof(RTGCUINTREG), 16, RTSF_INTW, 0 }, + { STRMEM("Hr"), sizeof(RTHCUINTREG), 16, RTSF_INTW, 0 }, { STRMEM("Hu"), sizeof(RTHCUINT), 10, RTSF_INT, 0 }, { STRMEM("Hv"), sizeof(RTHCPTR), 16, RTSF_INTW, 0 }, { STRMEM("Hx"), sizeof(RTHCUINT), 16, RTSF_INT, 0 }, @@ -394,24 +509,7 @@ DECLHIDDEN(size_t) rtstrFormatRt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, co case RTSF_IPV6: { if (VALID_PTR(u.pIpv6Addr)) - return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, - "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", - u.pIpv6Addr->au8[0], - u.pIpv6Addr->au8[1], - u.pIpv6Addr->au8[2], - u.pIpv6Addr->au8[3], - u.pIpv6Addr->au8[4], - u.pIpv6Addr->au8[5], - u.pIpv6Addr->au8[6], - u.pIpv6Addr->au8[7], - u.pIpv6Addr->au8[8], - u.pIpv6Addr->au8[9], - u.pIpv6Addr->au8[10], - u.pIpv6Addr->au8[11], - u.pIpv6Addr->au8[12], - u.pIpv6Addr->au8[13], - u.pIpv6Addr->au8[14], - u.pIpv6Addr->au8[15]); + return rtstrFormatIPv6(pfnOutput, pvArgOutput, u.pIpv6Addr); return pfnOutput(pvArgOutput, s_szNull, sizeof(s_szNull) - 1); } @@ -453,42 +551,11 @@ DECLHIDDEN(size_t) rtstrFormatRt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, co case RTNETADDRTYPE_IPV6: if (u.pNetAddr->uPort == RTNETADDR_PORT_NA) - return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, - "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", - u.pNetAddr->uAddr.IPv6.au8[0], - u.pNetAddr->uAddr.IPv6.au8[1], - u.pNetAddr->uAddr.IPv6.au8[2], - u.pNetAddr->uAddr.IPv6.au8[3], - u.pNetAddr->uAddr.IPv6.au8[4], - u.pNetAddr->uAddr.IPv6.au8[5], - u.pNetAddr->uAddr.IPv6.au8[6], - u.pNetAddr->uAddr.IPv6.au8[7], - u.pNetAddr->uAddr.IPv6.au8[8], - u.pNetAddr->uAddr.IPv6.au8[9], - u.pNetAddr->uAddr.IPv6.au8[10], - u.pNetAddr->uAddr.IPv6.au8[11], - u.pNetAddr->uAddr.IPv6.au8[12], - u.pNetAddr->uAddr.IPv6.au8[13], - u.pNetAddr->uAddr.IPv6.au8[14], - u.pNetAddr->uAddr.IPv6.au8[15]); + return rtstrFormatIPv6(pfnOutput, pvArgOutput, &u.pNetAddr->uAddr.IPv6); + return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, - "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x %u", - u.pNetAddr->uAddr.IPv6.au8[0], - u.pNetAddr->uAddr.IPv6.au8[1], - u.pNetAddr->uAddr.IPv6.au8[2], - u.pNetAddr->uAddr.IPv6.au8[3], - u.pNetAddr->uAddr.IPv6.au8[4], - u.pNetAddr->uAddr.IPv6.au8[5], - u.pNetAddr->uAddr.IPv6.au8[6], - u.pNetAddr->uAddr.IPv6.au8[7], - u.pNetAddr->uAddr.IPv6.au8[8], - u.pNetAddr->uAddr.IPv6.au8[9], - u.pNetAddr->uAddr.IPv6.au8[10], - u.pNetAddr->uAddr.IPv6.au8[11], - u.pNetAddr->uAddr.IPv6.au8[12], - u.pNetAddr->uAddr.IPv6.au8[13], - u.pNetAddr->uAddr.IPv6.au8[14], - u.pNetAddr->uAddr.IPv6.au8[15], + "[%RTnaipv6]:%u", + &u.pNetAddr->uAddr.IPv6, u.pNetAddr->uPort); case RTNETADDRTYPE_MAC: @@ -558,7 +625,7 @@ DECLHIDDEN(size_t) rtstrFormatRt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, co const char *pszLastSep; const char *psz = pszLastSep = va_arg(*pArgs, const char *); if (!VALID_PTR(psz)) - return pfnOutput(pvArgOutput, "<null>", sizeof("<null>") - 1); + return pfnOutput(pvArgOutput, RT_STR_TUPLE("<null>")); while ((ch = *psz) != '\0') { @@ -602,7 +669,7 @@ DECLHIDDEN(size_t) rtstrFormatRt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, co const char *pszStart; const char *psz = pszStart = va_arg(*pArgs, const char *); if (!VALID_PTR(psz)) - return pfnOutput(pvArgOutput, "<null>", sizeof("<null>") - 1); + return pfnOutput(pvArgOutput, RT_STR_TUPLE("<null>")); while ((ch = *psz) != '\0' && ch != '(') { @@ -664,7 +731,7 @@ DECLHIDDEN(size_t) rtstrFormatRt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, co while (off < cchPrecision) { int i; - cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%s%0*x %04x:", off ? "\n" : "", sizeof(pu8) * 2, (uintptr_t)pu8, off); + cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%s%0*p %04x:", off ? "\n" : "", sizeof(pu8) * 2, (uintptr_t)pu8, off); for (i = 0; i < cchWidth && off + i < cchPrecision ; i++) cch += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, off + i < cchPrecision ? !(i & 7) && i ? "-%02x" : " %02x" : " ", pu8[i]); @@ -707,7 +774,7 @@ DECLHIDDEN(size_t) rtstrFormatRt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, co } } else - return pfnOutput(pvArgOutput, "<null>", sizeof("<null>") - 1); + return pfnOutput(pvArgOutput, RT_STR_TUPLE("<null>")); break; } @@ -903,7 +970,7 @@ DECLHIDDEN(size_t) rtstrFormatRt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, co * If it's a pointer, we'll check if it's valid before going on. */ if ((s_aTypes[i].fFlags & RTST_FLAGS_POINTER) && !VALID_PTR(u.pv)) - return pfnOutput(pvArgOutput, "<null>", sizeof("<null>") - 1); + return pfnOutput(pvArgOutput, RT_STR_TUPLE("<null>")); /* * Format the output. @@ -1098,7 +1165,7 @@ DECLHIDDEN(size_t) rtstrFormatRt(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, co REG_OUT_BIT(cr4, X86_CR4_SMXE, "SMXE"); REG_OUT_BIT(cr4, X86_CR4_PCIDE, "PCIDE"); REG_OUT_BIT(cr4, X86_CR4_OSXSAVE, "OSXSAVE"); - REG_OUT_BIT(cr4, X86_CR4_SMEP, "SMPE"); + REG_OUT_BIT(cr4, X86_CR4_SMEP, "SMEP"); REG_OUT_CLOSE(cr4); } else diff --git a/src/VBox/Runtime/common/string/strformattype.cpp b/src/VBox/Runtime/common/string/strformattype.cpp index e9e3611a..c37f3b53 100644 --- a/src/VBox/Runtime/common/string/strformattype.cpp +++ b/src/VBox/Runtime/common/string/strformattype.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -466,9 +466,9 @@ DECLHIDDEN(size_t) rtstrFormatType(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput, { rtstrFormatTypeReadUnlock(); - cch = pfnOutput(pvArgOutput, "<missing:%R[", sizeof("<missing:%R[") - 1); + cch = pfnOutput(pvArgOutput, RT_STR_TUPLE("<missing:%R[")); cch += pfnOutput(pvArgOutput, pszType, pszTypeEnd - pszType); - cch += pfnOutput(pvArgOutput, "]>", sizeof("]>") - 1); + cch += pfnOutput(pvArgOutput, RT_STR_TUPLE("]>")); } return cch; diff --git a/src/VBox/Runtime/common/string/strlen.asm b/src/VBox/Runtime/common/string/strlen.asm index e3ff2f37..b34e5769 100644 --- a/src/VBox/Runtime/common/string/strlen.asm +++ b/src/VBox/Runtime/common/string/strlen.asm @@ -4,7 +4,7 @@ ; ; -; Copyright (C) 2006-2008 Oracle Corporation +; Copyright (C) 2006-2010 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strlen.cpp b/src/VBox/Runtime/common/string/strlen.cpp index 173ba2cb..792b040c 100644 --- a/src/VBox/Runtime/common/string/strlen.cpp +++ b/src/VBox/Runtime/common/string/strlen.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strlen_alias.c b/src/VBox/Runtime/common/string/strlen_alias.c index 8fab3d16..da471606 100644 --- a/src/VBox/Runtime/common/string/strlen_alias.c +++ b/src/VBox/Runtime/common/string/strlen_alias.c @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2008 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strncmp.cpp b/src/VBox/Runtime/common/string/strncmp.cpp index 16faf4b1..24d3fe43 100644 --- a/src/VBox/Runtime/common/string/strncmp.cpp +++ b/src/VBox/Runtime/common/string/strncmp.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strpbrk.cpp b/src/VBox/Runtime/common/string/strpbrk.cpp index e8d49a81..4e341ee9 100644 --- a/src/VBox/Runtime/common/string/strpbrk.cpp +++ b/src/VBox/Runtime/common/string/strpbrk.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strprintf.cpp b/src/VBox/Runtime/common/string/strprintf.cpp index 13bfd4b7..5e96e253 100644 --- a/src/VBox/Runtime/common/string/strprintf.cpp +++ b/src/VBox/Runtime/common/string/strprintf.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strspace.cpp b/src/VBox/Runtime/common/string/strspace.cpp index a27a417d..d76c1c19 100644 --- a/src/VBox/Runtime/common/string/strspace.cpp +++ b/src/VBox/Runtime/common/string/strspace.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strstrip.cpp b/src/VBox/Runtime/common/string/strstrip.cpp index 3f1b2809..9e930c8a 100644 --- a/src/VBox/Runtime/common/string/strstrip.cpp +++ b/src/VBox/Runtime/common/string/strstrip.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strtonum.cpp b/src/VBox/Runtime/common/string/strtonum.cpp index beb9b082..4d49a307 100644 --- a/src/VBox/Runtime/common/string/strtonum.cpp +++ b/src/VBox/Runtime/common/string/strtonum.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/strversion.cpp b/src/VBox/Runtime/common/string/strversion.cpp index 7a93bc8d..8c38c864 100644 --- a/src/VBox/Runtime/common/string/strversion.cpp +++ b/src/VBox/Runtime/common/string/strversion.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/uni.cpp b/src/VBox/Runtime/common/string/uni.cpp index 1fe613ec..d1bdb8b0 100644 --- a/src/VBox/Runtime/common/string/uni.cpp +++ b/src/VBox/Runtime/common/string/uni.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/unidata.cpp b/src/VBox/Runtime/common/string/unidata.cpp index ea5ce4ed..aa2679e2 100644 --- a/src/VBox/Runtime/common/string/unidata.cpp +++ b/src/VBox/Runtime/common/string/unidata.cpp @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/uniread.cpp b/src/VBox/Runtime/common/string/uniread.cpp index e532482f..01ddd1ca 100644 --- a/src/VBox/Runtime/common/string/uniread.cpp +++ b/src/VBox/Runtime/common/string/uniread.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -919,7 +919,7 @@ static int Stream1Printf(const char *pszFormat, ...) if (!g_fQuiet) cch = vfprintf(stdout, pszFormat, va); else - cch = strlen(pszFormat); + cch = (int)strlen(pszFormat); va_end(va); return cch; } diff --git a/src/VBox/Runtime/common/string/utf-16.cpp b/src/VBox/Runtime/common/string/utf-16.cpp index 3c89a3b0..9386b5d7 100644 --- a/src/VBox/Runtime/common/string/utf-16.cpp +++ b/src/VBox/Runtime/common/string/utf-16.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Runtime/common/string/utf-8-case.cpp b/src/VBox/Runtime/common/string/utf-8-case.cpp index 7a0bf2eb..e674944f 100644 --- a/src/VBox/Runtime/common/string/utf-8-case.cpp +++ b/src/VBox/Runtime/common/string/utf-8-case.cpp @@ -338,3 +338,82 @@ RTDECL(char *) RTStrToUpper(char *psz) } RT_EXPORT_SYMBOL(RTStrToUpper); + +RTDECL(bool) RTStrIsCaseFoldable(const char *psz) +{ + /* + * Loop the code points in the string, checking them one by one until we + * find something that can be folded. + */ + RTUNICP uc; + do + { + int rc = RTStrGetCpEx(&psz, &uc); + if (RT_SUCCESS(rc)) + { + if (RTUniCpIsFoldable(uc)) + return true; + } + else + { + /* bad encoding, just skip it quietly (uc == RTUNICP_INVALID (!= 0)). */ + AssertRC(rc); + } + } while (uc != 0); + + return false; +} +RT_EXPORT_SYMBOL(RTStrIsCaseFoldable); + + +RTDECL(bool) RTStrIsUpperCased(const char *psz) +{ + /* + * Check that there are no lower case chars in the string. + */ + RTUNICP uc; + do + { + int rc = RTStrGetCpEx(&psz, &uc); + if (RT_SUCCESS(rc)) + { + if (RTUniCpIsLower(uc)) + return false; + } + else + { + /* bad encoding, just skip it quietly (uc == RTUNICP_INVALID (!= 0)). */ + AssertRC(rc); + } + } while (uc != 0); + + return true; +} +RT_EXPORT_SYMBOL(RTStrIsUpperCased); + + +RTDECL(bool) RTStrIsLowerCased(const char *psz) +{ + /* + * Check that there are no lower case chars in the string. + */ + RTUNICP uc; + do + { + int rc = RTStrGetCpEx(&psz, &uc); + if (RT_SUCCESS(rc)) + { + if (RTUniCpIsUpper(uc)) + return false; + } + else + { + /* bad encoding, just skip it quietly (uc == RTUNICP_INVALID (!= 0)). */ + AssertRC(rc); + } + } while (uc != 0); + + return true; +} +RT_EXPORT_SYMBOL(RTStrIsLowerCased); + diff --git a/src/VBox/Runtime/common/string/utf-8.cpp b/src/VBox/Runtime/common/string/utf-8.cpp index 0e486e97..fdeb505e 100644 --- a/src/VBox/Runtime/common/string/utf-8.cpp +++ b/src/VBox/Runtime/common/string/utf-8.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; |