mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-19 07:26:26 +00:00
bug 95518, r=yokoyama@netscape.com,r=Roland.Mainz@informatik.med.uni-giessen.de,
sr=brendan@mozilla.org, Compress font char maps by folding the empty spaces on top of each other.
This commit is contained in:
parent
3bd4e80c52
commit
f53f35ce1d
@ -32,6 +32,7 @@ EXPORTS = \
|
||||
nsColor.h \
|
||||
nsColorNames.h \
|
||||
nsColorNameList.h \
|
||||
nsCompressedCharMap.h \
|
||||
nsCoord.h \
|
||||
nsFont.h \
|
||||
nsRect.h \
|
||||
|
246
gfx/public/nsCompressedCharMap.h
Normal file
246
gfx/public/nsCompressedCharMap.h
Normal file
@ -0,0 +1,246 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ex: set tabstop=8 softtabstop=2 shiftwidth=2 expandtab:
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 2001 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brian Stell <bstell@netscape.com>
|
||||
*/
|
||||
|
||||
#ifndef NSCOMPRESSEDCHARMAP_H
|
||||
#define NSCOMPRESSEDCHARMAP_H
|
||||
#include "prtypes.h"
|
||||
|
||||
|
||||
#define ALU_SIZE PR_BITS_PER_LONG
|
||||
//#define ALU_SIZE 16
|
||||
//#define ALU_SIZE 32
|
||||
//#define ALU_SIZE 64
|
||||
#if (ALU_SIZE==32)
|
||||
# define ALU_TYPE PRUint32
|
||||
# define CCMAP_POW2(n) (1L<<(n))
|
||||
# define CCMAP_BITS_PER_ALU_LOG2 5
|
||||
#elif (ALU_SIZE==64)
|
||||
# define ALU_TYPE PRUint64
|
||||
# define CCMAP_POW2(n) (1LL<<(n))
|
||||
# define CCMAP_BITS_PER_ALU_LOG2 6
|
||||
#else
|
||||
# define ALU_TYPE PRUint16
|
||||
# define CCMAP_POW2(n) (1<<(n))
|
||||
# define CCMAP_BITS_PER_ALU_LOG2 4
|
||||
#endif
|
||||
|
||||
|
||||
class nsICharRepresentable;
|
||||
|
||||
extern PRUint16* MapToCCMap(PRUint32* aMap);
|
||||
extern PRUint16* MapperToCCMap(nsICharRepresentable *aMapper);
|
||||
extern void FreeCCMap(PRUint16* &aMap);
|
||||
|
||||
//
|
||||
// nsCompressedCharMap
|
||||
//
|
||||
// A Compressed Char Map (CCMap) saves memory by folding all
|
||||
// the empty portions of the map on top of each other.
|
||||
//
|
||||
// Building a Compressed Char Map (CCMap) is more complex than
|
||||
// accessing it. We use the nsCompressedCharMap object to
|
||||
// build the CCMap. Once nsCompressedCharMap has built the CCMap
|
||||
// we get a copy of the CCMap and discard the nsCompressedCharMap
|
||||
// object. The CCMap is an array of PRUint16 and is accessed by
|
||||
// a macro.
|
||||
//
|
||||
// See "Character Map Compression" below for a discussion of
|
||||
// what the array looks like.
|
||||
|
||||
//
|
||||
// The maximum size a CCMap:
|
||||
// (16 upper pointers) + (16 empty mid pointers) +
|
||||
// (16 empty page) + (16*16 max mid pointers) +
|
||||
// (256*16 max pages) = 4400 PRUint16
|
||||
#define CCMAP_MAX_LEN (16+16+16+256+4096)
|
||||
|
||||
class nsCompressedCharMap {
|
||||
public:
|
||||
nsCompressedCharMap();
|
||||
|
||||
PRUint16* NewCCMap();
|
||||
void FreeCCMap(PRUint16*);
|
||||
void SetChar(PRUint16);
|
||||
void SetChars(PRUint16*);
|
||||
void SetChars(PRUint16, ALU_TYPE*);
|
||||
void SetChars(PRUint32*);
|
||||
|
||||
protected:
|
||||
PRUint16 mUsedLen; // in PRUint16
|
||||
PRUint16 mAllOnesPage;
|
||||
PRUint16 mCCMap[CCMAP_MAX_LEN];
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// Character Map Compression
|
||||
//
|
||||
// Each font requires its own 8k charmap. On a system with 200
|
||||
// fonts this would take: 200 * 8K = 1600K memory.
|
||||
//
|
||||
// Since most char maps are mostly empty a significant amount
|
||||
// of memory can be saved by not allocating the unused sections.
|
||||
//
|
||||
// If the map has one or more levels of indirection then the
|
||||
// the empty sections of the map can all be folded to a single
|
||||
// common empty element. In this way only the non-empty sections
|
||||
// need space. Because the empty sections actually point to a
|
||||
// common empty section every entry in the map can be valid
|
||||
// without requiring actually allocating space.
|
||||
// Some larger CJK fonts have large sections where every bit
|
||||
// is set. In the same way that the empty sections are folded
|
||||
// onto one "empty page", the sections where all bits are set are
|
||||
// folded on to one "all bits set page" .
|
||||
//
|
||||
// Break up the Unicode range bits 0x0000 - 0xFFFF
|
||||
// into 3 bit ranges:
|
||||
//
|
||||
// upper bits: bit15 - bit12
|
||||
// mid bits: bit11 - bit8
|
||||
// page bits: bit7 - bit0
|
||||
//
|
||||
// within a page, (assumming a 4 byte ALU)
|
||||
// bits 7-5 select one of the 8 longs
|
||||
// bits 4-0 select one of the 32 bits within the long
|
||||
//
|
||||
// There is exactly one upper "pointers" array.
|
||||
//
|
||||
// The upper pointers each point to a mid array. If there are no chars
|
||||
// in an upper pointer's block that pointer points to the empty mid.
|
||||
// Thus all upper pointers are "valid" even if they do not have space
|
||||
// allocated; eg: the accessor macro does not need to test if the
|
||||
// pointer is zero.
|
||||
//
|
||||
// Each mid pointer in the mid array points to a page. If there are no
|
||||
// chars in a mid pointer's page that pointer points to the empty page.
|
||||
// Thus all mid pointers are "valid" even if they do not have space
|
||||
// allocated; eg: the accessor macro does not need to test if the
|
||||
// pointer is zero.
|
||||
//
|
||||
// Since the array will be less than 5K PRUint16 the "pointers" can
|
||||
// be implemented as 2 byte offsets from the base instead of
|
||||
// real pointers.
|
||||
//
|
||||
// the format of the CCMap is
|
||||
// the upper pointers (16 PRUint16)
|
||||
// the empty mid pointers (16 PRUint16)
|
||||
// the empty page (16 PRUint16)
|
||||
// non-empty mid pointers and pages as needed
|
||||
|
||||
// One minor note: for a completely empty map it is actually
|
||||
// possible to fold the upper, empty mid, and empty page
|
||||
// on top of each other and make a map of only 32 bytes.
|
||||
|
||||
// offsets to the empty mid and empty page
|
||||
#define CCMAP_EMPTY_MID CCMAP_NUM_UPPER_POINTERS
|
||||
#define CCMAP_EMPTY_PAGE CCMAP_EMPTY_MID+CCMAP_NUM_MID_POINTERS
|
||||
|
||||
//
|
||||
// Because the table is offset based the code can build the table in a
|
||||
// temp space (max table size on the stack) and then do one alloc of
|
||||
// the actual needed size and simply copy over the data.
|
||||
//
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Page bits
|
||||
//
|
||||
#define CCMAP_BITS_PER_PAGE_LOG2 8
|
||||
#define CCMAP_BITS_PER_PAGE CCMAP_POW2(CCMAP_BITS_PER_PAGE_LOG2)
|
||||
#define CCMAP_BIT_INDEX(c) ((c) & PR_BITMASK(CCMAP_BITS_PER_ALU_LOG2))
|
||||
#define CCMAP_ALU_INDEX(c) (((c)>>CCMAP_BITS_PER_ALU_LOG2) \
|
||||
& PR_BITMASK(CCMAP_BITS_PER_PAGE_LOG2 - CCMAP_BITS_PER_ALU_LOG2))
|
||||
|
||||
#define CCMAP_PAGE_MASK PR_BITMASK(CCMAP_BITS_PER_PAGE_LOG2)
|
||||
#define CCMAP_NUM_PRUINT16S_PER_PAGE \
|
||||
(CCMAP_BITS_PER_PAGE/CCMAP_BITS_PER_PRUINT16)
|
||||
// one bit per char
|
||||
#define CCMAP_NUM_ALUS_PER_PAGE (CCMAP_BITS_PER_PAGE/CCMAP_BITS_PER_ALU)
|
||||
#define CCMAP_NUM_UCHARS_PER_PAGE CCMAP_BITS_PER_PAGE
|
||||
|
||||
//
|
||||
// Mid bits
|
||||
//
|
||||
#define CCMAP_BITS_PER_MID_LOG2 4
|
||||
#define CCMAP_MID_INDEX(c) \
|
||||
(((c)>>CCMAP_BITS_PER_PAGE_LOG2) & PR_BITMASK(CCMAP_BITS_PER_MID_LOG2))
|
||||
#define CCMAP_NUM_MID_POINTERS CCMAP_POW2(CCMAP_BITS_PER_MID_LOG2)
|
||||
#define CCMAP_NUM_UCHARS_PER_MID \
|
||||
CCMAP_POW2(CCMAP_BITS_PER_MID_LOG2+CCMAP_BITS_PER_PAGE_LOG2)
|
||||
|
||||
//
|
||||
// Upper bits
|
||||
//
|
||||
#define CCMAP_BITS_PER_UPPER_LOG2 4
|
||||
#define CCMAP_UPPER_INDEX(c) \
|
||||
(((c)>>(CCMAP_BITS_PER_MID_LOG2+CCMAP_BITS_PER_PAGE_LOG2)) \
|
||||
& PR_BITMASK(CCMAP_BITS_PER_UPPER_LOG2))
|
||||
#define CCMAP_NUM_UPPER_POINTERS CCMAP_POW2(CCMAP_BITS_PER_UPPER_LOG2)
|
||||
|
||||
//
|
||||
// Misc
|
||||
//
|
||||
#define CCMAP_BITS_PER_PRUINT16_LOG2 4
|
||||
#define CCMAP_BITS_PER_PRUINT32_LOG2 5
|
||||
|
||||
#define CCMAP_BITS_PER_PRUINT16 CCMAP_POW2(CCMAP_BITS_PER_PRUINT16_LOG2)
|
||||
#define CCMAP_BITS_PER_PRUINT32 CCMAP_POW2(CCMAP_BITS_PER_PRUINT32_LOG2)
|
||||
#define CCMAP_BITS_PER_ALU CCMAP_POW2(CCMAP_BITS_PER_ALU_LOG2)
|
||||
|
||||
#define CCMAP_ALUS_PER_PRUINT32 (CCMAP_BITS_PER_PRUINT32/CCMAP_BITS_PER_ALU)
|
||||
#define CCMAP_PRUINT32S_PER_ALU (CCMAP_BITS_PER_ALU/CCMAP_BITS_PER_PRUINT32)
|
||||
#define CCMAP_PRUINT32S_PER_PAGE (CCMAP_BITS_PER_PAGE/CCMAP_BITS_PER_PRUINT32)
|
||||
|
||||
#define CCMAP_ALU_MASK PR_BITMASK(CCMAP_BITS_PER_ALU)
|
||||
#define CCMAP_ALUS_PER_PAGE CCMAP_POW2(CCMAP_BITS_PER_PAGE_LOG2 \
|
||||
- CCMAP_BITS_PER_ALU_LOG2)
|
||||
#define NUM_UNICODE_CHARS CCMAP_POW2(CCMAP_BITS_PER_UPPER_LOG2 \
|
||||
+CCMAP_BITS_PER_MID_LOG2 \
|
||||
+CCMAP_BITS_PER_PAGE_LOG2)
|
||||
#define CCMAP_TOTAL_PAGES CCMAP_POW2(CCMAP_BITS_PER_UPPER_LOG2 \
|
||||
+CCMAP_BITS_PER_MID_LOG2)
|
||||
|
||||
|
||||
//
|
||||
// Finally, build up the macro to test the bit for a given char
|
||||
//
|
||||
|
||||
// offset from base to mid array
|
||||
#define CCMAP_TO_MID(m,c) ((m)[CCMAP_UPPER_INDEX(c)])
|
||||
|
||||
// offset from base to page
|
||||
#define CCMAP_TO_PAGE(m,c) ((m)[CCMAP_TO_MID((m),(c)) + CCMAP_MID_INDEX(c)])
|
||||
|
||||
// offset from base to alu
|
||||
#define CCMAP_TO_ALU(m,c) \
|
||||
(*((ALU_TYPE*)(&((m)[CCMAP_TO_PAGE((m),(c))])) + CCMAP_ALU_INDEX(c)))
|
||||
|
||||
// test the bit
|
||||
#define CCMAP_HAS_CHAR(m,c) (((CCMAP_TO_ALU(m,c))>>CCMAP_BIT_INDEX(c)) & 1)
|
||||
|
||||
// unset the bit
|
||||
#define CCMAP_UNSET_CHAR(m,c) (CCMAP_TO_ALU(m,c) &= ~(CCMAP_POW2(CCMAP_BIT_INDEX(c))))
|
||||
|
||||
#endif // NSCOMPRESSEDCHARMAP_H
|
@ -88,6 +88,7 @@ CPPSRCS = \
|
||||
nsBlender.cpp \
|
||||
nsColor.cpp \
|
||||
nsColorNames.cpp \
|
||||
nsCompressedCharMap.cpp \
|
||||
nsDeviceContext.cpp \
|
||||
nsFont.cpp \
|
||||
nsFontList.cpp \
|
||||
|
283
gfx/src/nsCompressedCharMap.cpp
Normal file
283
gfx/src/nsCompressedCharMap.cpp
Normal file
@ -0,0 +1,283 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ex: set tabstop=8 softtabstop=2 shiftwidth=2 expandtab:
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 2001 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brian Stell <bstell@netscape.com>
|
||||
*/
|
||||
|
||||
#include "prmem.h"
|
||||
#include "nsCompressedCharMap.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsICharRepresentable.h"
|
||||
|
||||
void
|
||||
FreeCCMap(PRUint16* &aMap)
|
||||
{
|
||||
if (!aMap)
|
||||
return;
|
||||
PR_Free(aMap);
|
||||
aMap = nsnull;
|
||||
}
|
||||
|
||||
PRUint16*
|
||||
MapToCCMap(PRUint32* aMap)
|
||||
{
|
||||
// put the data into a temp map
|
||||
nsCompressedCharMap ccmapObj;
|
||||
ccmapObj.SetChars(aMap);
|
||||
|
||||
// make a copy of the map
|
||||
PRUint16* ccmap = ccmapObj.NewCCMap();
|
||||
|
||||
#ifdef DEBUG
|
||||
for (int i=0; i<NUM_UNICODE_CHARS; i++) {
|
||||
PRBool oldb = IS_REPRESENTABLE(aMap, i);
|
||||
PRBool newb = CCMAP_HAS_CHAR(ccmap, i);
|
||||
if ((oldb) != (newb)) {
|
||||
NS_ASSERTION(oldb==newb,"failed to generate map correctly");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ccmap;
|
||||
}
|
||||
|
||||
PRUint16*
|
||||
MapperToCCMap(nsICharRepresentable *aMapper)
|
||||
{
|
||||
PRUint32 map[UCS2_MAP_LEN];
|
||||
memset(map, 0, sizeof(map));
|
||||
nsresult res = aMapper->FillInfo(map);
|
||||
if (NS_FAILED(res))
|
||||
return nsnull;
|
||||
PRUint16* ccMap = MapToCCMap(map);
|
||||
|
||||
return ccMap;
|
||||
}
|
||||
|
||||
PRUint16*
|
||||
nsCompressedCharMap::NewCCMap()
|
||||
{
|
||||
PRUint16 *newMap = (PRUint16*)PR_Malloc(mUsedLen * sizeof(PRUint16));
|
||||
NS_ASSERTION(newMap, "failed to alloc new CCMap");
|
||||
if (!newMap)
|
||||
return nsnull;
|
||||
|
||||
// transfer the data
|
||||
for (int i=0; i<mUsedLen; i++)
|
||||
newMap[i] = mCCMap[i];
|
||||
|
||||
return newMap;
|
||||
}
|
||||
|
||||
nsCompressedCharMap::nsCompressedCharMap()
|
||||
{
|
||||
// initialize map to have:
|
||||
// 1 upper pointer array
|
||||
// 1 empty mid pointer array
|
||||
// 1 empty page
|
||||
|
||||
memset(mCCMap, 0, sizeof(mCCMap));
|
||||
mUsedLen = 0;
|
||||
mAllOnesPage = 0;
|
||||
|
||||
// init the upper pointers
|
||||
PRUint16 *upper = &mCCMap[0];
|
||||
for (int i=0; i<CCMAP_NUM_UPPER_POINTERS; i++) {
|
||||
upper[i] = CCMAP_EMPTY_MID;
|
||||
}
|
||||
mUsedLen += CCMAP_NUM_UPPER_POINTERS;
|
||||
|
||||
// init the empty mid
|
||||
NS_ASSERTION(mUsedLen==CCMAP_EMPTY_MID, "empty mid offset misconfigured");
|
||||
PRUint16 *mid = &mCCMap[CCMAP_EMPTY_MID];
|
||||
for (int i=0; i<CCMAP_NUM_MID_POINTERS; i++) {
|
||||
mid[i] = CCMAP_EMPTY_PAGE;
|
||||
}
|
||||
mUsedLen += CCMAP_NUM_MID_POINTERS;
|
||||
|
||||
// init the empty page
|
||||
NS_ASSERTION(mUsedLen==CCMAP_EMPTY_PAGE, "empty page offset misconfigured");
|
||||
// the page was zero'd by the memset above
|
||||
mUsedLen += CCMAP_NUM_PRUINT16S_PER_PAGE;
|
||||
}
|
||||
|
||||
void
|
||||
nsCompressedCharMap::SetChar(PRUint16 aChar)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int upper_index = CCMAP_UPPER_INDEX(aChar);
|
||||
unsigned int mid_index = CCMAP_MID_INDEX(aChar);
|
||||
|
||||
PRUint16 mid_offset = mCCMap[upper_index];
|
||||
if (mid_offset == CCMAP_EMPTY_MID) {
|
||||
mid_offset = mCCMap[upper_index] = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_MID_POINTERS;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
// init the mid
|
||||
PRUint16 *mid = &mCCMap[mid_offset];
|
||||
for (i=0; i<CCMAP_NUM_MID_POINTERS; i++) {
|
||||
NS_ASSERTION(mid[i]==0, "this mid pointer should be unused");
|
||||
mid[i] = CCMAP_EMPTY_PAGE;
|
||||
}
|
||||
}
|
||||
|
||||
PRUint16 page_offset = mCCMap[mid_offset+mid_index];
|
||||
if (page_offset == CCMAP_EMPTY_PAGE) {
|
||||
page_offset = mCCMap[mid_offset+mid_index] = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_PRUINT16S_PER_PAGE;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
// init the page
|
||||
PRUint16 *page = &mCCMap[page_offset];
|
||||
for (i=0; i<CCMAP_NUM_PRUINT16S_PER_PAGE; i++) {
|
||||
NS_ASSERTION(page[i]==0, "this page should be unused");
|
||||
page[i] = 0;
|
||||
}
|
||||
}
|
||||
#undef CCMAP_SET_CHAR
|
||||
#define CCMAP_SET_CHAR(m,c) (CCMAP_TO_ALU(m,c) |= (CCMAP_POW2(CCMAP_BIT_INDEX(c))))
|
||||
CCMAP_SET_CHAR(mCCMap,aChar);
|
||||
#undef CCMAP_SET_CHAR
|
||||
NS_ASSERTION(CCMAP_HAS_CHAR(mCCMap,aChar), "failed to set bit");
|
||||
}
|
||||
|
||||
void
|
||||
nsCompressedCharMap::SetChars(PRUint16 aBase, ALU_TYPE* aPage)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int upper_index = CCMAP_UPPER_INDEX(aBase);
|
||||
unsigned int mid_index = CCMAP_MID_INDEX(aBase);
|
||||
NS_ASSERTION((aBase&CCMAP_PAGE_MASK)==0, "invalid page address");
|
||||
|
||||
//
|
||||
// check of none/all bits set
|
||||
//
|
||||
PRUint16 num_none_set = 0;
|
||||
PRUint16 num_all_set = 0;
|
||||
for (i=0; i<CCMAP_NUM_ALUS_PER_PAGE; i++) {
|
||||
if (aPage[i] == 0)
|
||||
num_none_set++;
|
||||
else if (aPage[i] == CCMAP_ALU_MASK)
|
||||
num_all_set++;
|
||||
}
|
||||
if (num_none_set == CCMAP_NUM_ALUS_PER_PAGE) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Alloc mid if necessary
|
||||
//
|
||||
PRUint16 mid_offset = mCCMap[upper_index];
|
||||
if (mid_offset == CCMAP_EMPTY_MID) {
|
||||
mid_offset = mCCMap[upper_index] = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_MID_POINTERS;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
// init the mid
|
||||
PRUint16 *mid = &mCCMap[mid_offset];
|
||||
for (i=0; i<CCMAP_NUM_MID_POINTERS; i++) {
|
||||
NS_ASSERTION(mid[i]==0, "this mid pointer should be unused");
|
||||
mid[i] = CCMAP_EMPTY_PAGE;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// if all bits set share an "all bits set" page
|
||||
//
|
||||
if (num_all_set == CCMAP_NUM_ALUS_PER_PAGE) {
|
||||
if (mAllOnesPage == 0) {
|
||||
mAllOnesPage = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_PRUINT16S_PER_PAGE;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
ALU_TYPE *all_ones_page = (ALU_TYPE*)&mCCMap[mAllOnesPage];
|
||||
for (i=0; i<CCMAP_NUM_ALUS_PER_PAGE; i++) {
|
||||
NS_ASSERTION(all_ones_page[i]==0, "this page should be unused");
|
||||
all_ones_page[i] = CCMAP_ALU_MASK;
|
||||
}
|
||||
}
|
||||
mCCMap[mid_offset+mid_index] = mAllOnesPage;
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Alloc page if necessary
|
||||
//
|
||||
PRUint16 page_offset = mCCMap[mid_offset+mid_index];
|
||||
if (page_offset == CCMAP_EMPTY_PAGE) {
|
||||
page_offset = mCCMap[mid_offset+mid_index] = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_PRUINT16S_PER_PAGE;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
}
|
||||
|
||||
// copy the page data
|
||||
ALU_TYPE *page = (ALU_TYPE*)&mCCMap[page_offset];
|
||||
for (i=0; i<CCMAP_NUM_ALUS_PER_PAGE; i++) {
|
||||
NS_ASSERTION(page[i]==0, "this page should be unused");
|
||||
page[i] = aPage[i];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsCompressedCharMap::SetChars(PRUint32* aMap)
|
||||
{
|
||||
PRUint32* frommap_page;
|
||||
frommap_page = aMap;
|
||||
PRUint16 base = 0;
|
||||
|
||||
for (int i=0; i<CCMAP_TOTAL_PAGES; i++) {
|
||||
|
||||
#if (CCMAP_BITS_PER_ALU == CCMAP_BITS_PER_PRUINT32)
|
||||
SetChars(base, (ALU_TYPE*)frommap_page);
|
||||
frommap_page += CCMAP_PRUINT32S_PER_PAGE;
|
||||
|
||||
#elif (CCMAP_BITS_PER_ALU > CCMAP_BITS_PER_PRUINT32)
|
||||
int j, k = CCMAP_BITS_PER_PRUINT32;
|
||||
ALU_TYPE page[CCMAP_NUM_ALUS_PER_PAGE];
|
||||
ALU_TYPE *p = page;
|
||||
for (j=0; j<CCMAP_ALUS_PER_PAGE; j++) {
|
||||
ALU_TYPE alu_val = 0;
|
||||
ALU_TYPE tmp;
|
||||
for (k=0; k<CCMAP_PRUINT32S_PER_ALU; k++) {
|
||||
tmp = *frommap_page;
|
||||
tmp <<= (k*CCMAP_BITS_PER_PRUINT32);
|
||||
//alu_val |= (*frommap_page)<<(k*CCMAP_BITS_PER_PRUINT32);
|
||||
alu_val |= tmp;
|
||||
frommap_page++;
|
||||
}
|
||||
*p++ = alu_val;
|
||||
}
|
||||
SetChars(base, page);
|
||||
#elif (CCMAP_BITS_PER_ALU < CCMAP_BITS_PER_PRUINT32)
|
||||
int j, k;
|
||||
ALU_TYPE page[CCMAP_NUM_ALUS_PER_PAGE];
|
||||
int v = CCMAP_PRUINT32S_PER_PAGE;
|
||||
ALU_TYPE *p = page;
|
||||
for (j=0; j<CCMAP_PRUINT32S_PER_PAGE; j++) {
|
||||
PRUint32 pruint32_val = *frommap_page++;
|
||||
for (k=0; k<CCMAP_ALUS_PER_PRUINT32; k++) {
|
||||
*p++ = pruint32_val & CCMAP_ALU_MASK;
|
||||
pruint32_val >>= CCMAP_BITS_PER_ALU;
|
||||
}
|
||||
}
|
||||
SetChars(base, page);
|
||||
#endif
|
||||
|
||||
base += CCMAP_NUM_UCHARS_PER_PAGE;
|
||||
}
|
||||
}
|
||||
|
283
gfx/src/shared/nsCompressedCharMap.cpp
Normal file
283
gfx/src/shared/nsCompressedCharMap.cpp
Normal file
@ -0,0 +1,283 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ex: set tabstop=8 softtabstop=2 shiftwidth=2 expandtab:
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 2001 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brian Stell <bstell@netscape.com>
|
||||
*/
|
||||
|
||||
#include "prmem.h"
|
||||
#include "nsCompressedCharMap.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsICharRepresentable.h"
|
||||
|
||||
void
|
||||
FreeCCMap(PRUint16* &aMap)
|
||||
{
|
||||
if (!aMap)
|
||||
return;
|
||||
PR_Free(aMap);
|
||||
aMap = nsnull;
|
||||
}
|
||||
|
||||
PRUint16*
|
||||
MapToCCMap(PRUint32* aMap)
|
||||
{
|
||||
// put the data into a temp map
|
||||
nsCompressedCharMap ccmapObj;
|
||||
ccmapObj.SetChars(aMap);
|
||||
|
||||
// make a copy of the map
|
||||
PRUint16* ccmap = ccmapObj.NewCCMap();
|
||||
|
||||
#ifdef DEBUG
|
||||
for (int i=0; i<NUM_UNICODE_CHARS; i++) {
|
||||
PRBool oldb = IS_REPRESENTABLE(aMap, i);
|
||||
PRBool newb = CCMAP_HAS_CHAR(ccmap, i);
|
||||
if ((oldb) != (newb)) {
|
||||
NS_ASSERTION(oldb==newb,"failed to generate map correctly");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ccmap;
|
||||
}
|
||||
|
||||
PRUint16*
|
||||
MapperToCCMap(nsICharRepresentable *aMapper)
|
||||
{
|
||||
PRUint32 map[UCS2_MAP_LEN];
|
||||
memset(map, 0, sizeof(map));
|
||||
nsresult res = aMapper->FillInfo(map);
|
||||
if (NS_FAILED(res))
|
||||
return nsnull;
|
||||
PRUint16* ccMap = MapToCCMap(map);
|
||||
|
||||
return ccMap;
|
||||
}
|
||||
|
||||
PRUint16*
|
||||
nsCompressedCharMap::NewCCMap()
|
||||
{
|
||||
PRUint16 *newMap = (PRUint16*)PR_Malloc(mUsedLen * sizeof(PRUint16));
|
||||
NS_ASSERTION(newMap, "failed to alloc new CCMap");
|
||||
if (!newMap)
|
||||
return nsnull;
|
||||
|
||||
// transfer the data
|
||||
for (int i=0; i<mUsedLen; i++)
|
||||
newMap[i] = mCCMap[i];
|
||||
|
||||
return newMap;
|
||||
}
|
||||
|
||||
nsCompressedCharMap::nsCompressedCharMap()
|
||||
{
|
||||
// initialize map to have:
|
||||
// 1 upper pointer array
|
||||
// 1 empty mid pointer array
|
||||
// 1 empty page
|
||||
|
||||
memset(mCCMap, 0, sizeof(mCCMap));
|
||||
mUsedLen = 0;
|
||||
mAllOnesPage = 0;
|
||||
|
||||
// init the upper pointers
|
||||
PRUint16 *upper = &mCCMap[0];
|
||||
for (int i=0; i<CCMAP_NUM_UPPER_POINTERS; i++) {
|
||||
upper[i] = CCMAP_EMPTY_MID;
|
||||
}
|
||||
mUsedLen += CCMAP_NUM_UPPER_POINTERS;
|
||||
|
||||
// init the empty mid
|
||||
NS_ASSERTION(mUsedLen==CCMAP_EMPTY_MID, "empty mid offset misconfigured");
|
||||
PRUint16 *mid = &mCCMap[CCMAP_EMPTY_MID];
|
||||
for (int i=0; i<CCMAP_NUM_MID_POINTERS; i++) {
|
||||
mid[i] = CCMAP_EMPTY_PAGE;
|
||||
}
|
||||
mUsedLen += CCMAP_NUM_MID_POINTERS;
|
||||
|
||||
// init the empty page
|
||||
NS_ASSERTION(mUsedLen==CCMAP_EMPTY_PAGE, "empty page offset misconfigured");
|
||||
// the page was zero'd by the memset above
|
||||
mUsedLen += CCMAP_NUM_PRUINT16S_PER_PAGE;
|
||||
}
|
||||
|
||||
void
|
||||
nsCompressedCharMap::SetChar(PRUint16 aChar)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int upper_index = CCMAP_UPPER_INDEX(aChar);
|
||||
unsigned int mid_index = CCMAP_MID_INDEX(aChar);
|
||||
|
||||
PRUint16 mid_offset = mCCMap[upper_index];
|
||||
if (mid_offset == CCMAP_EMPTY_MID) {
|
||||
mid_offset = mCCMap[upper_index] = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_MID_POINTERS;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
// init the mid
|
||||
PRUint16 *mid = &mCCMap[mid_offset];
|
||||
for (i=0; i<CCMAP_NUM_MID_POINTERS; i++) {
|
||||
NS_ASSERTION(mid[i]==0, "this mid pointer should be unused");
|
||||
mid[i] = CCMAP_EMPTY_PAGE;
|
||||
}
|
||||
}
|
||||
|
||||
PRUint16 page_offset = mCCMap[mid_offset+mid_index];
|
||||
if (page_offset == CCMAP_EMPTY_PAGE) {
|
||||
page_offset = mCCMap[mid_offset+mid_index] = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_PRUINT16S_PER_PAGE;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
// init the page
|
||||
PRUint16 *page = &mCCMap[page_offset];
|
||||
for (i=0; i<CCMAP_NUM_PRUINT16S_PER_PAGE; i++) {
|
||||
NS_ASSERTION(page[i]==0, "this page should be unused");
|
||||
page[i] = 0;
|
||||
}
|
||||
}
|
||||
#undef CCMAP_SET_CHAR
|
||||
#define CCMAP_SET_CHAR(m,c) (CCMAP_TO_ALU(m,c) |= (CCMAP_POW2(CCMAP_BIT_INDEX(c))))
|
||||
CCMAP_SET_CHAR(mCCMap,aChar);
|
||||
#undef CCMAP_SET_CHAR
|
||||
NS_ASSERTION(CCMAP_HAS_CHAR(mCCMap,aChar), "failed to set bit");
|
||||
}
|
||||
|
||||
void
|
||||
nsCompressedCharMap::SetChars(PRUint16 aBase, ALU_TYPE* aPage)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int upper_index = CCMAP_UPPER_INDEX(aBase);
|
||||
unsigned int mid_index = CCMAP_MID_INDEX(aBase);
|
||||
NS_ASSERTION((aBase&CCMAP_PAGE_MASK)==0, "invalid page address");
|
||||
|
||||
//
|
||||
// check of none/all bits set
|
||||
//
|
||||
PRUint16 num_none_set = 0;
|
||||
PRUint16 num_all_set = 0;
|
||||
for (i=0; i<CCMAP_NUM_ALUS_PER_PAGE; i++) {
|
||||
if (aPage[i] == 0)
|
||||
num_none_set++;
|
||||
else if (aPage[i] == CCMAP_ALU_MASK)
|
||||
num_all_set++;
|
||||
}
|
||||
if (num_none_set == CCMAP_NUM_ALUS_PER_PAGE) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Alloc mid if necessary
|
||||
//
|
||||
PRUint16 mid_offset = mCCMap[upper_index];
|
||||
if (mid_offset == CCMAP_EMPTY_MID) {
|
||||
mid_offset = mCCMap[upper_index] = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_MID_POINTERS;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
// init the mid
|
||||
PRUint16 *mid = &mCCMap[mid_offset];
|
||||
for (i=0; i<CCMAP_NUM_MID_POINTERS; i++) {
|
||||
NS_ASSERTION(mid[i]==0, "this mid pointer should be unused");
|
||||
mid[i] = CCMAP_EMPTY_PAGE;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// if all bits set share an "all bits set" page
|
||||
//
|
||||
if (num_all_set == CCMAP_NUM_ALUS_PER_PAGE) {
|
||||
if (mAllOnesPage == 0) {
|
||||
mAllOnesPage = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_PRUINT16S_PER_PAGE;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
ALU_TYPE *all_ones_page = (ALU_TYPE*)&mCCMap[mAllOnesPage];
|
||||
for (i=0; i<CCMAP_NUM_ALUS_PER_PAGE; i++) {
|
||||
NS_ASSERTION(all_ones_page[i]==0, "this page should be unused");
|
||||
all_ones_page[i] = CCMAP_ALU_MASK;
|
||||
}
|
||||
}
|
||||
mCCMap[mid_offset+mid_index] = mAllOnesPage;
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Alloc page if necessary
|
||||
//
|
||||
PRUint16 page_offset = mCCMap[mid_offset+mid_index];
|
||||
if (page_offset == CCMAP_EMPTY_PAGE) {
|
||||
page_offset = mCCMap[mid_offset+mid_index] = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_PRUINT16S_PER_PAGE;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
}
|
||||
|
||||
// copy the page data
|
||||
ALU_TYPE *page = (ALU_TYPE*)&mCCMap[page_offset];
|
||||
for (i=0; i<CCMAP_NUM_ALUS_PER_PAGE; i++) {
|
||||
NS_ASSERTION(page[i]==0, "this page should be unused");
|
||||
page[i] = aPage[i];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsCompressedCharMap::SetChars(PRUint32* aMap)
|
||||
{
|
||||
PRUint32* frommap_page;
|
||||
frommap_page = aMap;
|
||||
PRUint16 base = 0;
|
||||
|
||||
for (int i=0; i<CCMAP_TOTAL_PAGES; i++) {
|
||||
|
||||
#if (CCMAP_BITS_PER_ALU == CCMAP_BITS_PER_PRUINT32)
|
||||
SetChars(base, (ALU_TYPE*)frommap_page);
|
||||
frommap_page += CCMAP_PRUINT32S_PER_PAGE;
|
||||
|
||||
#elif (CCMAP_BITS_PER_ALU > CCMAP_BITS_PER_PRUINT32)
|
||||
int j, k = CCMAP_BITS_PER_PRUINT32;
|
||||
ALU_TYPE page[CCMAP_NUM_ALUS_PER_PAGE];
|
||||
ALU_TYPE *p = page;
|
||||
for (j=0; j<CCMAP_ALUS_PER_PAGE; j++) {
|
||||
ALU_TYPE alu_val = 0;
|
||||
ALU_TYPE tmp;
|
||||
for (k=0; k<CCMAP_PRUINT32S_PER_ALU; k++) {
|
||||
tmp = *frommap_page;
|
||||
tmp <<= (k*CCMAP_BITS_PER_PRUINT32);
|
||||
//alu_val |= (*frommap_page)<<(k*CCMAP_BITS_PER_PRUINT32);
|
||||
alu_val |= tmp;
|
||||
frommap_page++;
|
||||
}
|
||||
*p++ = alu_val;
|
||||
}
|
||||
SetChars(base, page);
|
||||
#elif (CCMAP_BITS_PER_ALU < CCMAP_BITS_PER_PRUINT32)
|
||||
int j, k;
|
||||
ALU_TYPE page[CCMAP_NUM_ALUS_PER_PAGE];
|
||||
int v = CCMAP_PRUINT32S_PER_PAGE;
|
||||
ALU_TYPE *p = page;
|
||||
for (j=0; j<CCMAP_PRUINT32S_PER_PAGE; j++) {
|
||||
PRUint32 pruint32_val = *frommap_page++;
|
||||
for (k=0; k<CCMAP_ALUS_PER_PRUINT32; k++) {
|
||||
*p++ = pruint32_val & CCMAP_ALU_MASK;
|
||||
pruint32_val >>= CCMAP_BITS_PER_ALU;
|
||||
}
|
||||
}
|
||||
SetChars(base, page);
|
||||
#endif
|
||||
|
||||
base += CCMAP_NUM_UCHARS_PER_PAGE;
|
||||
}
|
||||
}
|
||||
|
246
gfx/src/shared/nsCompressedCharMap.h
Normal file
246
gfx/src/shared/nsCompressedCharMap.h
Normal file
@ -0,0 +1,246 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ex: set tabstop=8 softtabstop=2 shiftwidth=2 expandtab:
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 2001 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brian Stell <bstell@netscape.com>
|
||||
*/
|
||||
|
||||
#ifndef NSCOMPRESSEDCHARMAP_H
|
||||
#define NSCOMPRESSEDCHARMAP_H
|
||||
#include "prtypes.h"
|
||||
|
||||
|
||||
#define ALU_SIZE PR_BITS_PER_LONG
|
||||
//#define ALU_SIZE 16
|
||||
//#define ALU_SIZE 32
|
||||
//#define ALU_SIZE 64
|
||||
#if (ALU_SIZE==32)
|
||||
# define ALU_TYPE PRUint32
|
||||
# define CCMAP_POW2(n) (1L<<(n))
|
||||
# define CCMAP_BITS_PER_ALU_LOG2 5
|
||||
#elif (ALU_SIZE==64)
|
||||
# define ALU_TYPE PRUint64
|
||||
# define CCMAP_POW2(n) (1LL<<(n))
|
||||
# define CCMAP_BITS_PER_ALU_LOG2 6
|
||||
#else
|
||||
# define ALU_TYPE PRUint16
|
||||
# define CCMAP_POW2(n) (1<<(n))
|
||||
# define CCMAP_BITS_PER_ALU_LOG2 4
|
||||
#endif
|
||||
|
||||
|
||||
class nsICharRepresentable;
|
||||
|
||||
extern PRUint16* MapToCCMap(PRUint32* aMap);
|
||||
extern PRUint16* MapperToCCMap(nsICharRepresentable *aMapper);
|
||||
extern void FreeCCMap(PRUint16* &aMap);
|
||||
|
||||
//
|
||||
// nsCompressedCharMap
|
||||
//
|
||||
// A Compressed Char Map (CCMap) saves memory by folding all
|
||||
// the empty portions of the map on top of each other.
|
||||
//
|
||||
// Building a Compressed Char Map (CCMap) is more complex than
|
||||
// accessing it. We use the nsCompressedCharMap object to
|
||||
// build the CCMap. Once nsCompressedCharMap has built the CCMap
|
||||
// we get a copy of the CCMap and discard the nsCompressedCharMap
|
||||
// object. The CCMap is an array of PRUint16 and is accessed by
|
||||
// a macro.
|
||||
//
|
||||
// See "Character Map Compression" below for a discussion of
|
||||
// what the array looks like.
|
||||
|
||||
//
|
||||
// The maximum size a CCMap:
|
||||
// (16 upper pointers) + (16 empty mid pointers) +
|
||||
// (16 empty page) + (16*16 max mid pointers) +
|
||||
// (256*16 max pages) = 4400 PRUint16
|
||||
#define CCMAP_MAX_LEN (16+16+16+256+4096)
|
||||
|
||||
class nsCompressedCharMap {
|
||||
public:
|
||||
nsCompressedCharMap();
|
||||
|
||||
PRUint16* NewCCMap();
|
||||
void FreeCCMap(PRUint16*);
|
||||
void SetChar(PRUint16);
|
||||
void SetChars(PRUint16*);
|
||||
void SetChars(PRUint16, ALU_TYPE*);
|
||||
void SetChars(PRUint32*);
|
||||
|
||||
protected:
|
||||
PRUint16 mUsedLen; // in PRUint16
|
||||
PRUint16 mAllOnesPage;
|
||||
PRUint16 mCCMap[CCMAP_MAX_LEN];
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// Character Map Compression
|
||||
//
|
||||
// Each font requires its own 8k charmap. On a system with 200
|
||||
// fonts this would take: 200 * 8K = 1600K memory.
|
||||
//
|
||||
// Since most char maps are mostly empty a significant amount
|
||||
// of memory can be saved by not allocating the unused sections.
|
||||
//
|
||||
// If the map has one or more levels of indirection then the
|
||||
// the empty sections of the map can all be folded to a single
|
||||
// common empty element. In this way only the non-empty sections
|
||||
// need space. Because the empty sections actually point to a
|
||||
// common empty section every entry in the map can be valid
|
||||
// without requiring actually allocating space.
|
||||
// Some larger CJK fonts have large sections where every bit
|
||||
// is set. In the same way that the empty sections are folded
|
||||
// onto one "empty page", the sections where all bits are set are
|
||||
// folded on to one "all bits set page" .
|
||||
//
|
||||
// Break up the Unicode range bits 0x0000 - 0xFFFF
|
||||
// into 3 bit ranges:
|
||||
//
|
||||
// upper bits: bit15 - bit12
|
||||
// mid bits: bit11 - bit8
|
||||
// page bits: bit7 - bit0
|
||||
//
|
||||
// within a page, (assumming a 4 byte ALU)
|
||||
// bits 7-5 select one of the 8 longs
|
||||
// bits 4-0 select one of the 32 bits within the long
|
||||
//
|
||||
// There is exactly one upper "pointers" array.
|
||||
//
|
||||
// The upper pointers each point to a mid array. If there are no chars
|
||||
// in an upper pointer's block that pointer points to the empty mid.
|
||||
// Thus all upper pointers are "valid" even if they do not have space
|
||||
// allocated; eg: the accessor macro does not need to test if the
|
||||
// pointer is zero.
|
||||
//
|
||||
// Each mid pointer in the mid array points to a page. If there are no
|
||||
// chars in a mid pointer's page that pointer points to the empty page.
|
||||
// Thus all mid pointers are "valid" even if they do not have space
|
||||
// allocated; eg: the accessor macro does not need to test if the
|
||||
// pointer is zero.
|
||||
//
|
||||
// Since the array will be less than 5K PRUint16 the "pointers" can
|
||||
// be implemented as 2 byte offsets from the base instead of
|
||||
// real pointers.
|
||||
//
|
||||
// the format of the CCMap is
|
||||
// the upper pointers (16 PRUint16)
|
||||
// the empty mid pointers (16 PRUint16)
|
||||
// the empty page (16 PRUint16)
|
||||
// non-empty mid pointers and pages as needed
|
||||
|
||||
// One minor note: for a completely empty map it is actually
|
||||
// possible to fold the upper, empty mid, and empty page
|
||||
// on top of each other and make a map of only 32 bytes.
|
||||
|
||||
// offsets to the empty mid and empty page
|
||||
#define CCMAP_EMPTY_MID CCMAP_NUM_UPPER_POINTERS
|
||||
#define CCMAP_EMPTY_PAGE CCMAP_EMPTY_MID+CCMAP_NUM_MID_POINTERS
|
||||
|
||||
//
|
||||
// Because the table is offset based the code can build the table in a
|
||||
// temp space (max table size on the stack) and then do one alloc of
|
||||
// the actual needed size and simply copy over the data.
|
||||
//
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Page bits
|
||||
//
|
||||
#define CCMAP_BITS_PER_PAGE_LOG2 8
|
||||
#define CCMAP_BITS_PER_PAGE CCMAP_POW2(CCMAP_BITS_PER_PAGE_LOG2)
|
||||
#define CCMAP_BIT_INDEX(c) ((c) & PR_BITMASK(CCMAP_BITS_PER_ALU_LOG2))
|
||||
#define CCMAP_ALU_INDEX(c) (((c)>>CCMAP_BITS_PER_ALU_LOG2) \
|
||||
& PR_BITMASK(CCMAP_BITS_PER_PAGE_LOG2 - CCMAP_BITS_PER_ALU_LOG2))
|
||||
|
||||
#define CCMAP_PAGE_MASK PR_BITMASK(CCMAP_BITS_PER_PAGE_LOG2)
|
||||
#define CCMAP_NUM_PRUINT16S_PER_PAGE \
|
||||
(CCMAP_BITS_PER_PAGE/CCMAP_BITS_PER_PRUINT16)
|
||||
// one bit per char
|
||||
#define CCMAP_NUM_ALUS_PER_PAGE (CCMAP_BITS_PER_PAGE/CCMAP_BITS_PER_ALU)
|
||||
#define CCMAP_NUM_UCHARS_PER_PAGE CCMAP_BITS_PER_PAGE
|
||||
|
||||
//
|
||||
// Mid bits
|
||||
//
|
||||
#define CCMAP_BITS_PER_MID_LOG2 4
|
||||
#define CCMAP_MID_INDEX(c) \
|
||||
(((c)>>CCMAP_BITS_PER_PAGE_LOG2) & PR_BITMASK(CCMAP_BITS_PER_MID_LOG2))
|
||||
#define CCMAP_NUM_MID_POINTERS CCMAP_POW2(CCMAP_BITS_PER_MID_LOG2)
|
||||
#define CCMAP_NUM_UCHARS_PER_MID \
|
||||
CCMAP_POW2(CCMAP_BITS_PER_MID_LOG2+CCMAP_BITS_PER_PAGE_LOG2)
|
||||
|
||||
//
|
||||
// Upper bits
|
||||
//
|
||||
#define CCMAP_BITS_PER_UPPER_LOG2 4
|
||||
#define CCMAP_UPPER_INDEX(c) \
|
||||
(((c)>>(CCMAP_BITS_PER_MID_LOG2+CCMAP_BITS_PER_PAGE_LOG2)) \
|
||||
& PR_BITMASK(CCMAP_BITS_PER_UPPER_LOG2))
|
||||
#define CCMAP_NUM_UPPER_POINTERS CCMAP_POW2(CCMAP_BITS_PER_UPPER_LOG2)
|
||||
|
||||
//
|
||||
// Misc
|
||||
//
|
||||
#define CCMAP_BITS_PER_PRUINT16_LOG2 4
|
||||
#define CCMAP_BITS_PER_PRUINT32_LOG2 5
|
||||
|
||||
#define CCMAP_BITS_PER_PRUINT16 CCMAP_POW2(CCMAP_BITS_PER_PRUINT16_LOG2)
|
||||
#define CCMAP_BITS_PER_PRUINT32 CCMAP_POW2(CCMAP_BITS_PER_PRUINT32_LOG2)
|
||||
#define CCMAP_BITS_PER_ALU CCMAP_POW2(CCMAP_BITS_PER_ALU_LOG2)
|
||||
|
||||
#define CCMAP_ALUS_PER_PRUINT32 (CCMAP_BITS_PER_PRUINT32/CCMAP_BITS_PER_ALU)
|
||||
#define CCMAP_PRUINT32S_PER_ALU (CCMAP_BITS_PER_ALU/CCMAP_BITS_PER_PRUINT32)
|
||||
#define CCMAP_PRUINT32S_PER_PAGE (CCMAP_BITS_PER_PAGE/CCMAP_BITS_PER_PRUINT32)
|
||||
|
||||
#define CCMAP_ALU_MASK PR_BITMASK(CCMAP_BITS_PER_ALU)
|
||||
#define CCMAP_ALUS_PER_PAGE CCMAP_POW2(CCMAP_BITS_PER_PAGE_LOG2 \
|
||||
- CCMAP_BITS_PER_ALU_LOG2)
|
||||
#define NUM_UNICODE_CHARS CCMAP_POW2(CCMAP_BITS_PER_UPPER_LOG2 \
|
||||
+CCMAP_BITS_PER_MID_LOG2 \
|
||||
+CCMAP_BITS_PER_PAGE_LOG2)
|
||||
#define CCMAP_TOTAL_PAGES CCMAP_POW2(CCMAP_BITS_PER_UPPER_LOG2 \
|
||||
+CCMAP_BITS_PER_MID_LOG2)
|
||||
|
||||
|
||||
//
|
||||
// Finally, build up the macro to test the bit for a given char
|
||||
//
|
||||
|
||||
// offset from base to mid array
|
||||
#define CCMAP_TO_MID(m,c) ((m)[CCMAP_UPPER_INDEX(c)])
|
||||
|
||||
// offset from base to page
|
||||
#define CCMAP_TO_PAGE(m,c) ((m)[CCMAP_TO_MID((m),(c)) + CCMAP_MID_INDEX(c)])
|
||||
|
||||
// offset from base to alu
|
||||
#define CCMAP_TO_ALU(m,c) \
|
||||
(*((ALU_TYPE*)(&((m)[CCMAP_TO_PAGE((m),(c))])) + CCMAP_ALU_INDEX(c)))
|
||||
|
||||
// test the bit
|
||||
#define CCMAP_HAS_CHAR(m,c) (((CCMAP_TO_ALU(m,c))>>CCMAP_BIT_INDEX(c)) & 1)
|
||||
|
||||
// unset the bit
|
||||
#define CCMAP_UNSET_CHAR(m,c) (CCMAP_TO_ALU(m,c) &= ~(CCMAP_POW2(CCMAP_BIT_INDEX(c))))
|
||||
|
||||
#endif // NSCOMPRESSEDCHARMAP_H
|
283
intl/unicharutil/util/nsCompressedCharMap.cpp
Normal file
283
intl/unicharutil/util/nsCompressedCharMap.cpp
Normal file
@ -0,0 +1,283 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ex: set tabstop=8 softtabstop=2 shiftwidth=2 expandtab:
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 2001 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brian Stell <bstell@netscape.com>
|
||||
*/
|
||||
|
||||
#include "prmem.h"
|
||||
#include "nsCompressedCharMap.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsICharRepresentable.h"
|
||||
|
||||
void
|
||||
FreeCCMap(PRUint16* &aMap)
|
||||
{
|
||||
if (!aMap)
|
||||
return;
|
||||
PR_Free(aMap);
|
||||
aMap = nsnull;
|
||||
}
|
||||
|
||||
PRUint16*
|
||||
MapToCCMap(PRUint32* aMap)
|
||||
{
|
||||
// put the data into a temp map
|
||||
nsCompressedCharMap ccmapObj;
|
||||
ccmapObj.SetChars(aMap);
|
||||
|
||||
// make a copy of the map
|
||||
PRUint16* ccmap = ccmapObj.NewCCMap();
|
||||
|
||||
#ifdef DEBUG
|
||||
for (int i=0; i<NUM_UNICODE_CHARS; i++) {
|
||||
PRBool oldb = IS_REPRESENTABLE(aMap, i);
|
||||
PRBool newb = CCMAP_HAS_CHAR(ccmap, i);
|
||||
if ((oldb) != (newb)) {
|
||||
NS_ASSERTION(oldb==newb,"failed to generate map correctly");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ccmap;
|
||||
}
|
||||
|
||||
PRUint16*
|
||||
MapperToCCMap(nsICharRepresentable *aMapper)
|
||||
{
|
||||
PRUint32 map[UCS2_MAP_LEN];
|
||||
memset(map, 0, sizeof(map));
|
||||
nsresult res = aMapper->FillInfo(map);
|
||||
if (NS_FAILED(res))
|
||||
return nsnull;
|
||||
PRUint16* ccMap = MapToCCMap(map);
|
||||
|
||||
return ccMap;
|
||||
}
|
||||
|
||||
PRUint16*
|
||||
nsCompressedCharMap::NewCCMap()
|
||||
{
|
||||
PRUint16 *newMap = (PRUint16*)PR_Malloc(mUsedLen * sizeof(PRUint16));
|
||||
NS_ASSERTION(newMap, "failed to alloc new CCMap");
|
||||
if (!newMap)
|
||||
return nsnull;
|
||||
|
||||
// transfer the data
|
||||
for (int i=0; i<mUsedLen; i++)
|
||||
newMap[i] = mCCMap[i];
|
||||
|
||||
return newMap;
|
||||
}
|
||||
|
||||
nsCompressedCharMap::nsCompressedCharMap()
|
||||
{
|
||||
// initialize map to have:
|
||||
// 1 upper pointer array
|
||||
// 1 empty mid pointer array
|
||||
// 1 empty page
|
||||
|
||||
memset(mCCMap, 0, sizeof(mCCMap));
|
||||
mUsedLen = 0;
|
||||
mAllOnesPage = 0;
|
||||
|
||||
// init the upper pointers
|
||||
PRUint16 *upper = &mCCMap[0];
|
||||
for (int i=0; i<CCMAP_NUM_UPPER_POINTERS; i++) {
|
||||
upper[i] = CCMAP_EMPTY_MID;
|
||||
}
|
||||
mUsedLen += CCMAP_NUM_UPPER_POINTERS;
|
||||
|
||||
// init the empty mid
|
||||
NS_ASSERTION(mUsedLen==CCMAP_EMPTY_MID, "empty mid offset misconfigured");
|
||||
PRUint16 *mid = &mCCMap[CCMAP_EMPTY_MID];
|
||||
for (int i=0; i<CCMAP_NUM_MID_POINTERS; i++) {
|
||||
mid[i] = CCMAP_EMPTY_PAGE;
|
||||
}
|
||||
mUsedLen += CCMAP_NUM_MID_POINTERS;
|
||||
|
||||
// init the empty page
|
||||
NS_ASSERTION(mUsedLen==CCMAP_EMPTY_PAGE, "empty page offset misconfigured");
|
||||
// the page was zero'd by the memset above
|
||||
mUsedLen += CCMAP_NUM_PRUINT16S_PER_PAGE;
|
||||
}
|
||||
|
||||
void
|
||||
nsCompressedCharMap::SetChar(PRUint16 aChar)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int upper_index = CCMAP_UPPER_INDEX(aChar);
|
||||
unsigned int mid_index = CCMAP_MID_INDEX(aChar);
|
||||
|
||||
PRUint16 mid_offset = mCCMap[upper_index];
|
||||
if (mid_offset == CCMAP_EMPTY_MID) {
|
||||
mid_offset = mCCMap[upper_index] = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_MID_POINTERS;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
// init the mid
|
||||
PRUint16 *mid = &mCCMap[mid_offset];
|
||||
for (i=0; i<CCMAP_NUM_MID_POINTERS; i++) {
|
||||
NS_ASSERTION(mid[i]==0, "this mid pointer should be unused");
|
||||
mid[i] = CCMAP_EMPTY_PAGE;
|
||||
}
|
||||
}
|
||||
|
||||
PRUint16 page_offset = mCCMap[mid_offset+mid_index];
|
||||
if (page_offset == CCMAP_EMPTY_PAGE) {
|
||||
page_offset = mCCMap[mid_offset+mid_index] = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_PRUINT16S_PER_PAGE;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
// init the page
|
||||
PRUint16 *page = &mCCMap[page_offset];
|
||||
for (i=0; i<CCMAP_NUM_PRUINT16S_PER_PAGE; i++) {
|
||||
NS_ASSERTION(page[i]==0, "this page should be unused");
|
||||
page[i] = 0;
|
||||
}
|
||||
}
|
||||
#undef CCMAP_SET_CHAR
|
||||
#define CCMAP_SET_CHAR(m,c) (CCMAP_TO_ALU(m,c) |= (CCMAP_POW2(CCMAP_BIT_INDEX(c))))
|
||||
CCMAP_SET_CHAR(mCCMap,aChar);
|
||||
#undef CCMAP_SET_CHAR
|
||||
NS_ASSERTION(CCMAP_HAS_CHAR(mCCMap,aChar), "failed to set bit");
|
||||
}
|
||||
|
||||
void
|
||||
nsCompressedCharMap::SetChars(PRUint16 aBase, ALU_TYPE* aPage)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int upper_index = CCMAP_UPPER_INDEX(aBase);
|
||||
unsigned int mid_index = CCMAP_MID_INDEX(aBase);
|
||||
NS_ASSERTION((aBase&CCMAP_PAGE_MASK)==0, "invalid page address");
|
||||
|
||||
//
|
||||
// check of none/all bits set
|
||||
//
|
||||
PRUint16 num_none_set = 0;
|
||||
PRUint16 num_all_set = 0;
|
||||
for (i=0; i<CCMAP_NUM_ALUS_PER_PAGE; i++) {
|
||||
if (aPage[i] == 0)
|
||||
num_none_set++;
|
||||
else if (aPage[i] == CCMAP_ALU_MASK)
|
||||
num_all_set++;
|
||||
}
|
||||
if (num_none_set == CCMAP_NUM_ALUS_PER_PAGE) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Alloc mid if necessary
|
||||
//
|
||||
PRUint16 mid_offset = mCCMap[upper_index];
|
||||
if (mid_offset == CCMAP_EMPTY_MID) {
|
||||
mid_offset = mCCMap[upper_index] = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_MID_POINTERS;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
// init the mid
|
||||
PRUint16 *mid = &mCCMap[mid_offset];
|
||||
for (i=0; i<CCMAP_NUM_MID_POINTERS; i++) {
|
||||
NS_ASSERTION(mid[i]==0, "this mid pointer should be unused");
|
||||
mid[i] = CCMAP_EMPTY_PAGE;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// if all bits set share an "all bits set" page
|
||||
//
|
||||
if (num_all_set == CCMAP_NUM_ALUS_PER_PAGE) {
|
||||
if (mAllOnesPage == 0) {
|
||||
mAllOnesPage = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_PRUINT16S_PER_PAGE;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
ALU_TYPE *all_ones_page = (ALU_TYPE*)&mCCMap[mAllOnesPage];
|
||||
for (i=0; i<CCMAP_NUM_ALUS_PER_PAGE; i++) {
|
||||
NS_ASSERTION(all_ones_page[i]==0, "this page should be unused");
|
||||
all_ones_page[i] = CCMAP_ALU_MASK;
|
||||
}
|
||||
}
|
||||
mCCMap[mid_offset+mid_index] = mAllOnesPage;
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Alloc page if necessary
|
||||
//
|
||||
PRUint16 page_offset = mCCMap[mid_offset+mid_index];
|
||||
if (page_offset == CCMAP_EMPTY_PAGE) {
|
||||
page_offset = mCCMap[mid_offset+mid_index] = mUsedLen;
|
||||
mUsedLen += CCMAP_NUM_PRUINT16S_PER_PAGE;
|
||||
NS_ASSERTION(mUsedLen<=CCMAP_MAX_LEN,"length too long");
|
||||
}
|
||||
|
||||
// copy the page data
|
||||
ALU_TYPE *page = (ALU_TYPE*)&mCCMap[page_offset];
|
||||
for (i=0; i<CCMAP_NUM_ALUS_PER_PAGE; i++) {
|
||||
NS_ASSERTION(page[i]==0, "this page should be unused");
|
||||
page[i] = aPage[i];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsCompressedCharMap::SetChars(PRUint32* aMap)
|
||||
{
|
||||
PRUint32* frommap_page;
|
||||
frommap_page = aMap;
|
||||
PRUint16 base = 0;
|
||||
|
||||
for (int i=0; i<CCMAP_TOTAL_PAGES; i++) {
|
||||
|
||||
#if (CCMAP_BITS_PER_ALU == CCMAP_BITS_PER_PRUINT32)
|
||||
SetChars(base, (ALU_TYPE*)frommap_page);
|
||||
frommap_page += CCMAP_PRUINT32S_PER_PAGE;
|
||||
|
||||
#elif (CCMAP_BITS_PER_ALU > CCMAP_BITS_PER_PRUINT32)
|
||||
int j, k = CCMAP_BITS_PER_PRUINT32;
|
||||
ALU_TYPE page[CCMAP_NUM_ALUS_PER_PAGE];
|
||||
ALU_TYPE *p = page;
|
||||
for (j=0; j<CCMAP_ALUS_PER_PAGE; j++) {
|
||||
ALU_TYPE alu_val = 0;
|
||||
ALU_TYPE tmp;
|
||||
for (k=0; k<CCMAP_PRUINT32S_PER_ALU; k++) {
|
||||
tmp = *frommap_page;
|
||||
tmp <<= (k*CCMAP_BITS_PER_PRUINT32);
|
||||
//alu_val |= (*frommap_page)<<(k*CCMAP_BITS_PER_PRUINT32);
|
||||
alu_val |= tmp;
|
||||
frommap_page++;
|
||||
}
|
||||
*p++ = alu_val;
|
||||
}
|
||||
SetChars(base, page);
|
||||
#elif (CCMAP_BITS_PER_ALU < CCMAP_BITS_PER_PRUINT32)
|
||||
int j, k;
|
||||
ALU_TYPE page[CCMAP_NUM_ALUS_PER_PAGE];
|
||||
int v = CCMAP_PRUINT32S_PER_PAGE;
|
||||
ALU_TYPE *p = page;
|
||||
for (j=0; j<CCMAP_PRUINT32S_PER_PAGE; j++) {
|
||||
PRUint32 pruint32_val = *frommap_page++;
|
||||
for (k=0; k<CCMAP_ALUS_PER_PRUINT32; k++) {
|
||||
*p++ = pruint32_val & CCMAP_ALU_MASK;
|
||||
pruint32_val >>= CCMAP_BITS_PER_ALU;
|
||||
}
|
||||
}
|
||||
SetChars(base, page);
|
||||
#endif
|
||||
|
||||
base += CCMAP_NUM_UCHARS_PER_PAGE;
|
||||
}
|
||||
}
|
||||
|
246
intl/unicharutil/util/nsCompressedCharMap.h
Normal file
246
intl/unicharutil/util/nsCompressedCharMap.h
Normal file
@ -0,0 +1,246 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* ex: set tabstop=8 softtabstop=2 shiftwidth=2 expandtab:
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape Communications
|
||||
* Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 2001 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brian Stell <bstell@netscape.com>
|
||||
*/
|
||||
|
||||
#ifndef NSCOMPRESSEDCHARMAP_H
|
||||
#define NSCOMPRESSEDCHARMAP_H
|
||||
#include "prtypes.h"
|
||||
|
||||
|
||||
#define ALU_SIZE PR_BITS_PER_LONG
|
||||
//#define ALU_SIZE 16
|
||||
//#define ALU_SIZE 32
|
||||
//#define ALU_SIZE 64
|
||||
#if (ALU_SIZE==32)
|
||||
# define ALU_TYPE PRUint32
|
||||
# define CCMAP_POW2(n) (1L<<(n))
|
||||
# define CCMAP_BITS_PER_ALU_LOG2 5
|
||||
#elif (ALU_SIZE==64)
|
||||
# define ALU_TYPE PRUint64
|
||||
# define CCMAP_POW2(n) (1LL<<(n))
|
||||
# define CCMAP_BITS_PER_ALU_LOG2 6
|
||||
#else
|
||||
# define ALU_TYPE PRUint16
|
||||
# define CCMAP_POW2(n) (1<<(n))
|
||||
# define CCMAP_BITS_PER_ALU_LOG2 4
|
||||
#endif
|
||||
|
||||
|
||||
class nsICharRepresentable;
|
||||
|
||||
extern PRUint16* MapToCCMap(PRUint32* aMap);
|
||||
extern PRUint16* MapperToCCMap(nsICharRepresentable *aMapper);
|
||||
extern void FreeCCMap(PRUint16* &aMap);
|
||||
|
||||
//
|
||||
// nsCompressedCharMap
|
||||
//
|
||||
// A Compressed Char Map (CCMap) saves memory by folding all
|
||||
// the empty portions of the map on top of each other.
|
||||
//
|
||||
// Building a Compressed Char Map (CCMap) is more complex than
|
||||
// accessing it. We use the nsCompressedCharMap object to
|
||||
// build the CCMap. Once nsCompressedCharMap has built the CCMap
|
||||
// we get a copy of the CCMap and discard the nsCompressedCharMap
|
||||
// object. The CCMap is an array of PRUint16 and is accessed by
|
||||
// a macro.
|
||||
//
|
||||
// See "Character Map Compression" below for a discussion of
|
||||
// what the array looks like.
|
||||
|
||||
//
|
||||
// The maximum size a CCMap:
|
||||
// (16 upper pointers) + (16 empty mid pointers) +
|
||||
// (16 empty page) + (16*16 max mid pointers) +
|
||||
// (256*16 max pages) = 4400 PRUint16
|
||||
#define CCMAP_MAX_LEN (16+16+16+256+4096)
|
||||
|
||||
class nsCompressedCharMap {
|
||||
public:
|
||||
nsCompressedCharMap();
|
||||
|
||||
PRUint16* NewCCMap();
|
||||
void FreeCCMap(PRUint16*);
|
||||
void SetChar(PRUint16);
|
||||
void SetChars(PRUint16*);
|
||||
void SetChars(PRUint16, ALU_TYPE*);
|
||||
void SetChars(PRUint32*);
|
||||
|
||||
protected:
|
||||
PRUint16 mUsedLen; // in PRUint16
|
||||
PRUint16 mAllOnesPage;
|
||||
PRUint16 mCCMap[CCMAP_MAX_LEN];
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// Character Map Compression
|
||||
//
|
||||
// Each font requires its own 8k charmap. On a system with 200
|
||||
// fonts this would take: 200 * 8K = 1600K memory.
|
||||
//
|
||||
// Since most char maps are mostly empty a significant amount
|
||||
// of memory can be saved by not allocating the unused sections.
|
||||
//
|
||||
// If the map has one or more levels of indirection then the
|
||||
// the empty sections of the map can all be folded to a single
|
||||
// common empty element. In this way only the non-empty sections
|
||||
// need space. Because the empty sections actually point to a
|
||||
// common empty section every entry in the map can be valid
|
||||
// without requiring actually allocating space.
|
||||
// Some larger CJK fonts have large sections where every bit
|
||||
// is set. In the same way that the empty sections are folded
|
||||
// onto one "empty page", the sections where all bits are set are
|
||||
// folded on to one "all bits set page" .
|
||||
//
|
||||
// Break up the Unicode range bits 0x0000 - 0xFFFF
|
||||
// into 3 bit ranges:
|
||||
//
|
||||
// upper bits: bit15 - bit12
|
||||
// mid bits: bit11 - bit8
|
||||
// page bits: bit7 - bit0
|
||||
//
|
||||
// within a page, (assumming a 4 byte ALU)
|
||||
// bits 7-5 select one of the 8 longs
|
||||
// bits 4-0 select one of the 32 bits within the long
|
||||
//
|
||||
// There is exactly one upper "pointers" array.
|
||||
//
|
||||
// The upper pointers each point to a mid array. If there are no chars
|
||||
// in an upper pointer's block that pointer points to the empty mid.
|
||||
// Thus all upper pointers are "valid" even if they do not have space
|
||||
// allocated; eg: the accessor macro does not need to test if the
|
||||
// pointer is zero.
|
||||
//
|
||||
// Each mid pointer in the mid array points to a page. If there are no
|
||||
// chars in a mid pointer's page that pointer points to the empty page.
|
||||
// Thus all mid pointers are "valid" even if they do not have space
|
||||
// allocated; eg: the accessor macro does not need to test if the
|
||||
// pointer is zero.
|
||||
//
|
||||
// Since the array will be less than 5K PRUint16 the "pointers" can
|
||||
// be implemented as 2 byte offsets from the base instead of
|
||||
// real pointers.
|
||||
//
|
||||
// the format of the CCMap is
|
||||
// the upper pointers (16 PRUint16)
|
||||
// the empty mid pointers (16 PRUint16)
|
||||
// the empty page (16 PRUint16)
|
||||
// non-empty mid pointers and pages as needed
|
||||
|
||||
// One minor note: for a completely empty map it is actually
|
||||
// possible to fold the upper, empty mid, and empty page
|
||||
// on top of each other and make a map of only 32 bytes.
|
||||
|
||||
// offsets to the empty mid and empty page
|
||||
#define CCMAP_EMPTY_MID CCMAP_NUM_UPPER_POINTERS
|
||||
#define CCMAP_EMPTY_PAGE CCMAP_EMPTY_MID+CCMAP_NUM_MID_POINTERS
|
||||
|
||||
//
|
||||
// Because the table is offset based the code can build the table in a
|
||||
// temp space (max table size on the stack) and then do one alloc of
|
||||
// the actual needed size and simply copy over the data.
|
||||
//
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Page bits
|
||||
//
|
||||
#define CCMAP_BITS_PER_PAGE_LOG2 8
|
||||
#define CCMAP_BITS_PER_PAGE CCMAP_POW2(CCMAP_BITS_PER_PAGE_LOG2)
|
||||
#define CCMAP_BIT_INDEX(c) ((c) & PR_BITMASK(CCMAP_BITS_PER_ALU_LOG2))
|
||||
#define CCMAP_ALU_INDEX(c) (((c)>>CCMAP_BITS_PER_ALU_LOG2) \
|
||||
& PR_BITMASK(CCMAP_BITS_PER_PAGE_LOG2 - CCMAP_BITS_PER_ALU_LOG2))
|
||||
|
||||
#define CCMAP_PAGE_MASK PR_BITMASK(CCMAP_BITS_PER_PAGE_LOG2)
|
||||
#define CCMAP_NUM_PRUINT16S_PER_PAGE \
|
||||
(CCMAP_BITS_PER_PAGE/CCMAP_BITS_PER_PRUINT16)
|
||||
// one bit per char
|
||||
#define CCMAP_NUM_ALUS_PER_PAGE (CCMAP_BITS_PER_PAGE/CCMAP_BITS_PER_ALU)
|
||||
#define CCMAP_NUM_UCHARS_PER_PAGE CCMAP_BITS_PER_PAGE
|
||||
|
||||
//
|
||||
// Mid bits
|
||||
//
|
||||
#define CCMAP_BITS_PER_MID_LOG2 4
|
||||
#define CCMAP_MID_INDEX(c) \
|
||||
(((c)>>CCMAP_BITS_PER_PAGE_LOG2) & PR_BITMASK(CCMAP_BITS_PER_MID_LOG2))
|
||||
#define CCMAP_NUM_MID_POINTERS CCMAP_POW2(CCMAP_BITS_PER_MID_LOG2)
|
||||
#define CCMAP_NUM_UCHARS_PER_MID \
|
||||
CCMAP_POW2(CCMAP_BITS_PER_MID_LOG2+CCMAP_BITS_PER_PAGE_LOG2)
|
||||
|
||||
//
|
||||
// Upper bits
|
||||
//
|
||||
#define CCMAP_BITS_PER_UPPER_LOG2 4
|
||||
#define CCMAP_UPPER_INDEX(c) \
|
||||
(((c)>>(CCMAP_BITS_PER_MID_LOG2+CCMAP_BITS_PER_PAGE_LOG2)) \
|
||||
& PR_BITMASK(CCMAP_BITS_PER_UPPER_LOG2))
|
||||
#define CCMAP_NUM_UPPER_POINTERS CCMAP_POW2(CCMAP_BITS_PER_UPPER_LOG2)
|
||||
|
||||
//
|
||||
// Misc
|
||||
//
|
||||
#define CCMAP_BITS_PER_PRUINT16_LOG2 4
|
||||
#define CCMAP_BITS_PER_PRUINT32_LOG2 5
|
||||
|
||||
#define CCMAP_BITS_PER_PRUINT16 CCMAP_POW2(CCMAP_BITS_PER_PRUINT16_LOG2)
|
||||
#define CCMAP_BITS_PER_PRUINT32 CCMAP_POW2(CCMAP_BITS_PER_PRUINT32_LOG2)
|
||||
#define CCMAP_BITS_PER_ALU CCMAP_POW2(CCMAP_BITS_PER_ALU_LOG2)
|
||||
|
||||
#define CCMAP_ALUS_PER_PRUINT32 (CCMAP_BITS_PER_PRUINT32/CCMAP_BITS_PER_ALU)
|
||||
#define CCMAP_PRUINT32S_PER_ALU (CCMAP_BITS_PER_ALU/CCMAP_BITS_PER_PRUINT32)
|
||||
#define CCMAP_PRUINT32S_PER_PAGE (CCMAP_BITS_PER_PAGE/CCMAP_BITS_PER_PRUINT32)
|
||||
|
||||
#define CCMAP_ALU_MASK PR_BITMASK(CCMAP_BITS_PER_ALU)
|
||||
#define CCMAP_ALUS_PER_PAGE CCMAP_POW2(CCMAP_BITS_PER_PAGE_LOG2 \
|
||||
- CCMAP_BITS_PER_ALU_LOG2)
|
||||
#define NUM_UNICODE_CHARS CCMAP_POW2(CCMAP_BITS_PER_UPPER_LOG2 \
|
||||
+CCMAP_BITS_PER_MID_LOG2 \
|
||||
+CCMAP_BITS_PER_PAGE_LOG2)
|
||||
#define CCMAP_TOTAL_PAGES CCMAP_POW2(CCMAP_BITS_PER_UPPER_LOG2 \
|
||||
+CCMAP_BITS_PER_MID_LOG2)
|
||||
|
||||
|
||||
//
|
||||
// Finally, build up the macro to test the bit for a given char
|
||||
//
|
||||
|
||||
// offset from base to mid array
|
||||
#define CCMAP_TO_MID(m,c) ((m)[CCMAP_UPPER_INDEX(c)])
|
||||
|
||||
// offset from base to page
|
||||
#define CCMAP_TO_PAGE(m,c) ((m)[CCMAP_TO_MID((m),(c)) + CCMAP_MID_INDEX(c)])
|
||||
|
||||
// offset from base to alu
|
||||
#define CCMAP_TO_ALU(m,c) \
|
||||
(*((ALU_TYPE*)(&((m)[CCMAP_TO_PAGE((m),(c))])) + CCMAP_ALU_INDEX(c)))
|
||||
|
||||
// test the bit
|
||||
#define CCMAP_HAS_CHAR(m,c) (((CCMAP_TO_ALU(m,c))>>CCMAP_BIT_INDEX(c)) & 1)
|
||||
|
||||
// unset the bit
|
||||
#define CCMAP_UNSET_CHAR(m,c) (CCMAP_TO_ALU(m,c) &= ~(CCMAP_POW2(CCMAP_BIT_INDEX(c))))
|
||||
|
||||
#endif // NSCOMPRESSEDCHARMAP_H
|
Loading…
x
Reference in New Issue
Block a user