Start work on multibyte-support in locale backend.

* add ICUThreadLocaleStorageValue, which will be used to maintain
  per-thread ICU converters
* add ICUConverterManager
This commit is contained in:
Oliver Tappe 2011-11-22 16:55:39 +01:00
parent 02606f712c
commit bcadc4ca66
5 changed files with 372 additions and 1 deletions

View File

@ -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 <pthread.h>
#include <map>
#include <unicode/ucnv.h>
#include <SupportDefs.h>
#include <locks.h>
#include <Referenceable.h>
#include <util/DoublyLinkedList.h>
//#include <util/OpenHashTable.h>
#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<ICUConverterInfo> 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<LinkedConverterInfo>
{
public:
LinkedConverterInfo(UConverter* converter, const char* charset,
ICUConverterID id)
:
ICUConverterInfo(converter, charset, id)
{
}
};
typedef std::map<ICUConverterID, LinkedConverterInfo*> ConverterMap;
typedef DoublyLinkedList<LinkedConverterInfo> ConverterList;
private:
ConverterMap fConverterMap;
ConverterList fLRUConverters;
mutex fMutex;
ICUConverterID fNextConverterID;
};
} // namespace Libroot
} // namespace BPrivate
#endif // _ICU_CONVERTER_MANAGER_H

View File

@ -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 <pthread.h>
#include <SupportDefs.h>
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

View File

@ -0,0 +1,174 @@
/*
* Copyright 2011, Oliver Tappe, zooey@hirschkaefer.de.
* Distributed under the terms of the MIT License.
*/
#include "ICUConverterManager.h"
#include <pthread.h>
#include <new>
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

View File

@ -0,0 +1,52 @@
/*
* Copyright 2011, Oliver Tappe, zooey@hirschkaefer.de.
* Distributed under the terms of the MIT License.
*/
#include "ICUThreadLocalStorageValue.h"
#include <new>
#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<ICUThreadLocalStorageValue*>(value);
instanceOut = tlsValue;
return B_OK;
}
} // namespace Libroot
} // namespace BPrivate

View File

@ -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)
;