diff --git a/headers/private/libroot/locale/ICUConverterManager.h b/headers/private/libroot/locale/ICUConverterManager.h new file mode 100644 index 0000000000..86fcbf6938 --- /dev/null +++ b/headers/private/libroot/locale/ICUConverterManager.h @@ -0,0 +1,106 @@ +/* + * Copyright 2010, Oliver Tappe, zooey@hirschkaefer.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _ICU_CONVERTER_MANAGER_H +#define _ICU_CONVERTER_MANAGER_H + + +#include + +#include + +#include + +#include + +#include +#include +#include +//#include + +#include "ICUThreadLocalStorageValue.h" + + +namespace BPrivate { +namespace Libroot { + + +class ICUConverterInfo : public BReferenceable { +public: + ICUConverterInfo(UConverter* converter, + const char* charset, ICUConverterID id); + virtual ~ICUConverterInfo(); + + UConverter* Converter() const + { return fConverter; } + + const char* Charset() const + { return fCharset; } + + ICUConverterID ID() const + { return fID; } + +private: + UConverter* fConverter; + char fCharset[UCNV_MAX_CONVERTER_NAME_LENGTH]; + ICUConverterID fID; +}; + + +typedef BReference ICUConverterRef; + + +class ICUConverterManager { +public: + ICUConverterManager(); + ~ICUConverterManager(); + + status_t CreateConverter(const char* charset, + ICUConverterRef& converterRefOut, + ICUConverterID& idOut); + + status_t GetConverter(ICUConverterID id, + ICUConverterRef& converterRefOut); + + status_t DropConverter(ICUConverterID id); + + static ICUConverterManager* Instance(); + +private: + static void _CreateInstance(); + + static ICUConverterManager* sInstance; + + static const size_t skMaxConvertersPerProcess = 1024; + +private: + class LinkedConverterInfo + : + public ICUConverterInfo, + public DoublyLinkedListLinkImpl + { + public: + LinkedConverterInfo(UConverter* converter, const char* charset, + ICUConverterID id) + : + ICUConverterInfo(converter, charset, id) + { + } + }; + typedef std::map ConverterMap; + typedef DoublyLinkedList ConverterList; + +private: + ConverterMap fConverterMap; + ConverterList fLRUConverters; + mutex fMutex; + ICUConverterID fNextConverterID; +}; + + +} // namespace Libroot +} // namespace BPrivate + + +#endif // _ICU_CONVERTER_MANAGER_H diff --git a/headers/private/libroot/locale/ICUThreadLocalStorageValue.h b/headers/private/libroot/locale/ICUThreadLocalStorageValue.h new file mode 100644 index 0000000000..5ea3c26264 --- /dev/null +++ b/headers/private/libroot/locale/ICUThreadLocalStorageValue.h @@ -0,0 +1,36 @@ +/* + * Copyright 2011, Oliver Tappe, zooey@hirschkaefer.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _ICU_THREAD_LOCAL_STORAGE_VALUE_H +#define _ICU_THREAD_LOCAL_STORAGE_VALUE_H + + +#include + +#include + + +namespace BPrivate { +namespace Libroot { + + +typedef unsigned int ICUConverterID; + + +struct ICUThreadLocalStorageValue { + ICUConverterID converterID; + + ICUThreadLocalStorageValue(); + ~ICUThreadLocalStorageValue(); + + static status_t GetInstanceForKey(pthread_key_t tlsKey, + ICUThreadLocalStorageValue*& instanceOut); +}; + + +} // namespace Libroot +} // namespace BPrivate + + +#endif // _ICU_THREAD_LOCAL_STORAGE_VALUE_H diff --git a/src/system/libroot/add-ons/icu/ICUConverterManager.cpp b/src/system/libroot/add-ons/icu/ICUConverterManager.cpp new file mode 100644 index 0000000000..c4b750cda4 --- /dev/null +++ b/src/system/libroot/add-ons/icu/ICUConverterManager.cpp @@ -0,0 +1,174 @@ +/* + * Copyright 2011, Oliver Tappe, zooey@hirschkaefer.de. + * Distributed under the terms of the MIT License. + */ + + +#include "ICUConverterManager.h" + +#include + +#include + + +namespace BPrivate { +namespace Libroot { + + +static pthread_once_t sManagerInitOnce = PTHREAD_ONCE_INIT; + + +ICUConverterInfo::ICUConverterInfo(UConverter* converter, const char* charset, + ICUConverterID id) + : + fConverter(converter), + fID(id) +{ + strlcpy(fCharset, charset, sizeof(fCharset)); +} + + +ICUConverterInfo::~ICUConverterInfo() +{ + if (fConverter != NULL) + ucnv_close(fConverter); +} + + +ICUConverterManager* ICUConverterManager::sInstance = NULL; + + +ICUConverterManager::ICUConverterManager() + : + fNextConverterID(1) +{ + mutex_init(&fMutex, "ConverterManagerMutex"); +} + + +ICUConverterManager::~ICUConverterManager() +{ + ConverterMap::iterator iter; + for (iter = fConverterMap.begin(); iter != fConverterMap.end(); ++iter) + iter->second->ReleaseReference(); + + mutex_destroy(&fMutex); +} + + +status_t +ICUConverterManager::CreateConverter(const char* charset, + ICUConverterRef& converterRefOut, ICUConverterID& idOut) +{ + MutexLocker lock(fMutex); + if (!lock.IsLocked()) + return B_ERROR; + + UErrorCode icuStatus = U_ZERO_ERROR; + UConverter* icuConverter = ucnv_open(charset, &icuStatus); + if (icuConverter == NULL) + return B_NAME_NOT_FOUND; + + LinkedConverterInfo* converterInfo = new (std::nothrow) LinkedConverterInfo( + icuConverter, charset, fNextConverterID); + if (converterInfo == NULL) { + ucnv_close(icuConverter); + return B_NO_MEMORY; + } + ICUConverterRef converterRef(converterInfo, true); + + // setup the new converter to stop upon any errors + icuStatus = U_ZERO_ERROR; + ucnv_setToUCallBack(icuConverter, UCNV_TO_U_CALLBACK_STOP, NULL, NULL, NULL, + &icuStatus); + if (!U_SUCCESS(icuStatus)) + return B_ERROR; + icuStatus = U_ZERO_ERROR; + ucnv_setFromUCallBack(icuConverter, UCNV_FROM_U_CALLBACK_STOP, NULL, NULL, + NULL, &icuStatus); + if (!U_SUCCESS(icuStatus)) + return B_ERROR; + + // ok, we've got the converter, add it to our map + try { + if (fConverterMap.size() >= skMaxConvertersPerProcess) { + // make room by dropping the least recently used converter + LinkedConverterInfo* leastUsedConverter = fLRUConverters.Head(); + fLRUConverters.Remove(leastUsedConverter); + fConverterMap.erase(leastUsedConverter->ID()); + leastUsedConverter->ReleaseReference(); + } + fConverterMap[fNextConverterID] = converterInfo; + fLRUConverters.Insert(converterInfo); + } catch (...) { + return B_NO_MEMORY; + } + + converterRefOut = converterRef; + idOut = fNextConverterID++; + + converterRef.Detach(); + + return B_OK; +} + + +status_t +ICUConverterManager::GetConverter(ICUConverterID id, + ICUConverterRef& converterRefOut) +{ + MutexLocker lock(fMutex); + if (!lock.IsLocked()) + return B_ERROR; + + ConverterMap::iterator iter = fConverterMap.find(id); + if (iter == fConverterMap.end()) + return B_NAME_NOT_FOUND; + + converterRefOut.SetTo(iter->second); + + return B_OK; +} + + +status_t +ICUConverterManager::DropConverter(ICUConverterID id) +{ + MutexLocker lock(fMutex); + if (!lock.IsLocked()) + return B_ERROR; + + ConverterMap::iterator iter = fConverterMap.find(id); + if (iter == fConverterMap.end()) + return B_NAME_NOT_FOUND; + + fLRUConverters.Remove(iter->second); + fConverterMap.erase(iter); + iter->second->ReleaseReference(); + + return B_OK; +} + + +/*static*/ ICUConverterManager* +ICUConverterManager::Instance() +{ + if (sInstance != NULL) + return sInstance; + + pthread_once(&sManagerInitOnce, + &BPrivate::Libroot::ICUConverterManager::_CreateInstance); + + return sInstance; +} + + +/*static*/ void +ICUConverterManager::_CreateInstance() +{ + sInstance = new (std::nothrow) ICUConverterManager; +} + + +} // namespace Libroot +} // namespace BPrivate diff --git a/src/system/libroot/add-ons/icu/ICUThreadLocalStorageValue.cpp b/src/system/libroot/add-ons/icu/ICUThreadLocalStorageValue.cpp new file mode 100644 index 0000000000..d88410b6d7 --- /dev/null +++ b/src/system/libroot/add-ons/icu/ICUThreadLocalStorageValue.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2011, Oliver Tappe, zooey@hirschkaefer.de. + * Distributed under the terms of the MIT License. + */ + + +#include "ICUThreadLocalStorageValue.h" + +#include + +#include "ICUConverterManager.h" + + +namespace BPrivate { +namespace Libroot { + + +ICUThreadLocalStorageValue::ICUThreadLocalStorageValue() + : converterID(0) +{ +} + + +ICUThreadLocalStorageValue::~ICUThreadLocalStorageValue() +{ + if (converterID != 0) + ICUConverterManager::Instance()->DropConverter(converterID); +} + + +status_t +ICUThreadLocalStorageValue::GetInstanceForKey(pthread_key_t tlsKey, + ICUThreadLocalStorageValue*& instanceOut) +{ + ICUThreadLocalStorageValue* tlsValue = NULL; + void* value = pthread_getspecific(tlsKey); + if (value == NULL) { + tlsValue = new (std::nothrow) ICUThreadLocalStorageValue(); + if (tlsValue == NULL) + return B_NO_MEMORY; + pthread_setspecific(tlsKey, tlsValue); + } else + tlsValue = static_cast(value); + + instanceOut = tlsValue; + + return B_OK; +} + + +} // namespace Libroot +} // namespace BPrivate diff --git a/src/system/libroot/add-ons/icu/Jamfile b/src/system/libroot/add-ons/icu/Jamfile index 2ab0ec9d76..5640a26c29 100644 --- a/src/system/libroot/add-ons/icu/Jamfile +++ b/src/system/libroot/add-ons/icu/Jamfile @@ -1,6 +1,7 @@ SubDir HAIKU_TOP src system libroot add-ons icu ; UsePrivateHeaders + kernel libroot [ FDirName libroot locale ] [ FDirName libroot time ] @@ -10,12 +11,14 @@ UsePrivateHeaders local sources = ICUCategoryData.cpp ICUCollateData.cpp + ICUConverterManager.cpp ICUCtypeData.cpp ICULocaleBackend.cpp ICULocaleconvData.cpp ICUMessagesData.cpp ICUMonetaryData.cpp ICUNumericData.cpp + ICUThreadLocalStorageValue.cpp ICUTimeConversion.cpp ICUTimeData.cpp ; @@ -28,6 +31,6 @@ Includes [ FGristFiles $(sources) ] : $(HAIKU_ICU_HEADERS_DEPENDENCY) ; SharedLibrary libroot-addon-icu.so : $(sources) : - $(TARGET_LIBSTDC++) $(HAIKU_ICU_LIBS) + libreferenceable.a $(TARGET_LIBSTDC++) $(HAIKU_ICU_LIBS) ;