From dcc497d146a31c1e5a11b15722220c1825df7318 Mon Sep 17 00:00:00 2001 From: poire-z Date: Sun, 13 Jan 2019 19:35:33 +0100 Subject: [PATCH 1/8] EPUB: workaround ZIP files with corrupted central directories If the main reading method (using zip header at the end of the archive) fails, we can try using the alternative method already used when this zip header is missing ("truncated"), which uses local zip headers met along while scanning the zip. --- crengine/src/lvstream.cpp | 42 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/crengine/src/lvstream.cpp b/crengine/src/lvstream.cpp index 9fb12948e8..a457a57b45 100644 --- a/crengine/src/lvstream.cpp +++ b/crengine/src/lvstream.cpp @@ -2097,6 +2097,10 @@ struct ZipHd2 lUInt16 getZIPAttr() { return _Attr_and_Offset[0]; } lUInt32 getAttr() { return _Attr_and_Offset[1] | ((lUInt32)_Attr_and_Offset[2]<<16); } lUInt32 getOffset() { return _Attr_and_Offset[3] | ((lUInt32)_Attr_and_Offset[4]<<16); } + void setOffset(lUInt32 offset) { + _Attr_and_Offset[3] = (lUInt16)(offset & 0xFFFF); + _Attr_and_Offset[4] = (lUInt16)(offset >> 16); + } void byteOrderConv() { // @@ -2489,14 +2493,20 @@ class LVZipDecodeStream : public LVNamedStream class LVZipArc : public LVArcContainerBase { +protected: + // whether the alternative "truncated" method was used, or is to be used + bool m_alt_reading_method = false; public: + bool usedAltReadingMethod() { return m_alt_reading_method; } + void useAltReadingMethod() { m_alt_reading_method = true; } + virtual LVStreamRef OpenStream( const wchar_t * fname, lvopen_mode_t /*mode*/ ) { if ( fname[0]=='/' ) fname++; int found_index = -1; for (int i=0; iGetName() ) ) { + if ( m_list[i]->GetName() != NULL && !lStr_cmp( fname, m_list[i]->GetName() ) ) { if ( m_list[i]->IsContainer() ) { // found directory with same name!!! return LVStreamRef(); @@ -2592,6 +2602,18 @@ class LVZipArc : public LVArcContainerBase } truncated = !found; + + // If the main reading method (using zip header at the end of the + // archive) failed, we can try using the alternative method used + // when this zip header is missing ("truncated"), which uses + // local zip headers met along while scanning the zip. + if (m_alt_reading_method) + truncated = true; // do as if truncated + else if (truncated) // archive detected as truncated + // flag that, so there's no need to try that alt method, + // as it was used on first scan + m_alt_reading_method = true; + if (truncated) NextPosition=0; @@ -2612,6 +2634,11 @@ class LVZipArc : public LVArcContainerBase if (truncated) { + // The offset (that we don't find in a local header, but + // that we will store in the ZipHeader we're building) + // happens to be the current position here. + lUInt32 offset = (lUInt32)m_stream->GetPos(); + m_stream->Read( &ZipHd1, ZipHd1_size, &ReadSize); ZipHd1.byteOrderConv(); @@ -2636,6 +2663,10 @@ class LVZipArc : public LVArcContainerBase ZipHeader.NameLen=ZipHd1.getNameLen(); ZipHeader.AddLen=ZipHd1.getAddLen(); ZipHeader.Method=ZipHd1.getMethod(); + ZipHeader.setOffset(offset); + // We may get a last invalid record with NameLen=0, which shouldn't hurt. + // If it does, use: + // if (ZipHeader.NameLen == 0) break; } else { m_stream->Read( &ZipHeader, ZipHeader_size, &ReadSize); @@ -2742,8 +2773,17 @@ class LVZipArc : public LVArcContainerBase return NULL; LVZipArc * arc = new LVZipArc( stream ); int itemCount = arc->ReadContents(); + if ( itemCount > 0 && arc->usedAltReadingMethod() ) { + printf("CRE WARNING: zip file truncated: going on with possibly partial content.\n"); + } + else if ( itemCount <= 0 && !arc->usedAltReadingMethod() ) { + printf("CRE WARNING: zip file corrupted or invalid: trying alternative processing...\n"); + arc->useAltReadingMethod(); + itemCount = arc->ReadContents(); + } if ( itemCount <= 0 ) { + printf("CRE WARNING: zip file corrupted or invalid: processing failure.\n"); delete arc; return NULL; } From 6d0699a8e5f3851647f26a60708675fe66a32a17 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Wed, 13 Nov 2019 14:21:40 +0400 Subject: [PATCH 2/8] Small refactoring in commit 4173245dee739e403178faa297cb0d162349130c --- crengine/src/lvstream.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crengine/src/lvstream.cpp b/crengine/src/lvstream.cpp index a457a57b45..3f927364a5 100644 --- a/crengine/src/lvstream.cpp +++ b/crengine/src/lvstream.cpp @@ -2497,8 +2497,8 @@ class LVZipArc : public LVArcContainerBase // whether the alternative "truncated" method was used, or is to be used bool m_alt_reading_method = false; public: - bool usedAltReadingMethod() { return m_alt_reading_method; } - void useAltReadingMethod() { m_alt_reading_method = true; } + bool isAltReadingMethod() { return m_alt_reading_method; } + void setAltReadingMethod() { m_alt_reading_method = true; } virtual LVStreamRef OpenStream( const wchar_t * fname, lvopen_mode_t /*mode*/ ) { @@ -2773,17 +2773,17 @@ class LVZipArc : public LVArcContainerBase return NULL; LVZipArc * arc = new LVZipArc( stream ); int itemCount = arc->ReadContents(); - if ( itemCount > 0 && arc->usedAltReadingMethod() ) { - printf("CRE WARNING: zip file truncated: going on with possibly partial content.\n"); + if ( itemCount > 0 && arc->isAltReadingMethod() ) { + CRLog::warn("Zip file truncated: going on with possibly partial content."); } - else if ( itemCount <= 0 && !arc->usedAltReadingMethod() ) { - printf("CRE WARNING: zip file corrupted or invalid: trying alternative processing...\n"); - arc->useAltReadingMethod(); + else if ( itemCount <= 0 && !arc->isAltReadingMethod() ) { + CRLog::warn("Zip file corrupted or invalid: trying alternative processing..."); + arc->setAltReadingMethod(); itemCount = arc->ReadContents(); } if ( itemCount <= 0 ) { - printf("CRE WARNING: zip file corrupted or invalid: processing failure.\n"); + CRLog::error("Zip file corrupted or invalid: processing failure."); delete arc; return NULL; } From a349a5722506179ae5916cfdbfddb5614e34b9a1 Mon Sep 17 00:00:00 2001 From: poire-z Date: Sun, 13 Jan 2019 19:41:19 +0100 Subject: [PATCH 3/8] Supports book text encoded in WTF-8 https://en.wikipedia.org/wiki/UTF-8#WTF-8 WTF-8 is a superset of UTF-8, that includes UTF-16 surrogates in UTF-8 bytes (forbidden in well-formed UTF-8). We may get UTF-8 with these from bad producers or converters. (cherry picked from commit 7145b8625f2f24b49ee5391f578e2993c10f92fd) --- crengine/src/lvstring.cpp | 74 ++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/crengine/src/lvstring.cpp b/crengine/src/lvstring.cpp index 3bf3a7ae09..17053d254c 100644 --- a/crengine/src/lvstring.cpp +++ b/crengine/src/lvstring.cpp @@ -2915,6 +2915,9 @@ static void DecodeUtf8(const char * s, lChar16 * p, int len) } } +// Top two bits are 10, i.e. original & 11000000(2) == 10000000(2) +#define IS_FOLLOWING(index) ((s[index] & 0xC0) == 0x80) + void Utf8ToUnicode(const lUInt8 * src, int &srclen, lChar16 * dst, int &dstlen) { const lUInt8 * s = src; @@ -2922,37 +2925,84 @@ void Utf8ToUnicode(const lUInt8 * src, int &srclen, lChar16 * dst, int &dstlen) lChar16 * p = dst; lChar16 * endp = p + dstlen; lUInt32 ch; + bool matched; while (p < endp && s < ends) { ch = *s; + matched = false; if ( (ch & 0x80) == 0 ) { + matched = true; *p++ = (char)ch; s++; } else if ( (ch & 0xE0) == 0xC0 ) { if (s + 2 > ends) break; - *p++ = ((ch & 0x1F) << 6) - | CONT_BYTE(1,0); - s += 2; + if (IS_FOLLOWING(1)) { + matched = true; + *p++ = ((ch & 0x1F) << 6) + | CONT_BYTE(1,0); + s += 2; + } } else if ( (ch & 0xF0) == 0xE0 ) { if (s + 3 > ends) break; - *p++ = ((ch & 0x0F) << 12) - | CONT_BYTE(1,6) - | CONT_BYTE(2,0); - s += 3; + if (IS_FOLLOWING(1) && IS_FOLLOWING(2)) { + matched = true; + *p++ = ((ch & 0x0F) << 12) + | CONT_BYTE(1,6) + | CONT_BYTE(2,0); + s += 3; + // Supports WTF-8 : https://en.wikipedia.org/wiki/UTF-8#WTF-8 + // a superset of UTF-8, that includes UTF-16 surrogates + // in UTF-8 bytes (forbidden in well-formed UTF-8). + // We may get that from bad producers or converters. + // As these shouldn't be there in UTF-8, if we find + // these surrogates in the right sequence, we might as well + // convert the char they represent to the right Unicode + // codepoint and display it instead of a '?'. + // Surrogates are code points from two special ranges of + // Unicode values, reserved for use as the leading, and + // trailing values of paired code units in UTF-16. Leading, + // also called high, surrogates are from D800 to DBFF, and + // trailing, or low, surrogates are from DC00 to DFFF. They + // are called surrogates, since they do not represent + // characters directly, but only as a pair. + // (Note that lChar16 (wchar_t) is 4-bytes, and can store + // unicode codepoint > 0xFFFF like 0x10123) + if (*(p-1) >= 0xD800 && *(p-1) <= 0xDBFF && s+2 < ends) { // what we wrote is a high surrogate, + lUInt32 next = *s; // and there's room next for a low surrogate + if ( (next & 0xF0) == 0xE0 && IS_FOLLOWING(1) && IS_FOLLOWING(2)) { // is a valid 3-bytes sequence + next = ((next & 0x0F) << 12) | CONT_BYTE(1,6) | CONT_BYTE(2,0); + if (next >= 0xDC00 && next <= 0xDFFF) { // is a low surrogate: valid surrogates sequence + ch = 0x10000 + ((*(p-1) & 0x3FF)<<10) + (next & 0x3FF); + p--; // rewind to override what we wrote + *p++ = ch; + s += 3; + } + } + } + } } else if ( (ch & 0xF8) == 0xF0 ) { if (s + 4 > ends) break; - *p++ = ((ch & 0x07) << 18) - | CONT_BYTE(1,12) - | CONT_BYTE(2,6) - | CONT_BYTE(3,0); - s += 4; + if (IS_FOLLOWING(1) && IS_FOLLOWING(2) && IS_FOLLOWING(3)) { + matched = true; + *p++ = ((ch & 0x07) << 18) + | CONT_BYTE(1,12) + | CONT_BYTE(2,6) + | CONT_BYTE(3,0); + s += 4; + } } else { // Invalid first byte in UTF-8 sequence // Pass with mask 0x7F, to resolve exception around env->NewStringUTF() *p++ = (char) (ch & 0x7F); s++; + matched = true; // just to avoid next if + } + // unexpected character + if (!matched) { + *p++ = '?'; + s++; } } srclen = (int)(s - src); From 6b83c1dd6f55f100e1fd1504cbb88ff5379e4964 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Sat, 21 Dec 2019 13:30:39 +0400 Subject: [PATCH 4/8] Function prototypes LVFont::getGlyphInfo() LVFont::getGlyph() were changed to exclude integer overflow when passing arguments, including characters with codes greater than 0xFFFF. --- crengine/include/lvfont.h | 9 +++++---- crengine/src/private/lvbitmapfont.cpp | 6 +++--- crengine/src/private/lvbitmapfont.h | 6 +++--- crengine/src/private/lvfontboldtransform.cpp | 4 ++-- crengine/src/private/lvfontboldtransform.h | 4 ++-- crengine/src/private/lvfreetypeface.cpp | 6 +++--- crengine/src/private/lvfreetypeface.h | 8 ++++---- crengine/src/private/lvwin32font.cpp | 8 ++++---- crengine/src/private/lvwin32font.h | 10 +++++----- 9 files changed, 31 insertions(+), 30 deletions(-) diff --git a/crengine/include/lvfont.h b/crengine/include/lvfont.h index be17282755..7a7cc726db 100644 --- a/crengine/include/lvfont.h +++ b/crengine/include/lvfont.h @@ -67,10 +67,11 @@ class LVFont : public LVRefCounter { virtual int getVisualAligmentWidth(); /** \brief get glyph info + \param code is unicode character code \param glyph is pointer to glyph_info_t struct to place retrieved info \return true if glyh was found */ - virtual bool getGlyphInfo(lUInt16 code, glyph_info_t *glyph, lChar16 def_char = 0) = 0; + virtual bool getGlyphInfo(lUInt32 code, glyph_info_t *glyph, lChar16 def_char = 0) = 0; /** \brief measure text \param text is text string pointer @@ -104,12 +105,12 @@ class LVFont : public LVRefCounter { // \param buf is buffer [width*height] to place glyph data // \return true if glyph was found // */ -// virtual bool getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0) = 0; +// virtual bool getGlyphImage(lUInt32 code, lUInt8 * buf, lChar16 def_char=0) = 0; /** \brief get glyph item - \param code is unicode character + \param code is unicode character code \return glyph pointer if glyph was found, NULL otherwise */ - virtual LVFontGlyphCacheItem *getGlyph(lUInt16 ch, lChar16 def_char = 0) = 0; + virtual LVFontGlyphCacheItem *getGlyph(lUInt32 ch, lChar16 def_char = 0) = 0; /// returns font baseline offset virtual int getBaseline() = 0; diff --git a/crengine/src/private/lvbitmapfont.cpp b/crengine/src/private/lvbitmapfont.cpp index 96c61e6880..363176231e 100644 --- a/crengine/src/private/lvbitmapfont.cpp +++ b/crengine/src/private/lvbitmapfont.cpp @@ -32,7 +32,7 @@ LVFontRef LoadFontFromFile( const char * fname ) return ref; } -bool LBitmapFont::getGlyphInfo( lUInt16 code, LVFont::glyph_info_t * glyph, lChar16 def_char ) +bool LBitmapFont::getGlyphInfo( lUInt32 code, LVFont::glyph_info_t * glyph, lChar16 def_char ) { const lvfont_glyph_t * ptr = lvfontGetGlyph( m_font, code ); if (!ptr) @@ -110,7 +110,7 @@ int LBitmapFont::getItalic() const return hdr->flgItalic; } /* -bool LBitmapFont::getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char) +bool LBitmapFont::getGlyphImage(lUInt32 code, lUInt8 * buf, lChar16 def_char) { const lvfont_glyph_t * ptr = lvfontGetGlyph( m_font, code ); if (!ptr) @@ -122,7 +122,7 @@ bool LBitmapFont::getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char) return true; } */ -LVFontGlyphCacheItem *LBitmapFont::getGlyph(lUInt16 ch, lChar16 def_char) { +LVFontGlyphCacheItem *LBitmapFont::getGlyph(lUInt32 ch, lChar16 def_char) { // TODO: return NULL; } diff --git a/crengine/src/private/lvbitmapfont.h b/crengine/src/private/lvbitmapfont.h index 4c5c487a23..9b06f4b93e 100644 --- a/crengine/src/private/lvbitmapfont.h +++ b/crengine/src/private/lvbitmapfont.h @@ -30,7 +30,7 @@ class LBitmapFont : public LVBaseFont { public: LBitmapFont() : m_font(NULL) {} - virtual bool getGlyphInfo(lUInt16 code, LVFont::glyph_info_t *glyph, lChar16 def_char = 0); + virtual bool getGlyphInfo(lUInt32 code, LVFont::glyph_info_t *glyph, lChar16 def_char = 0); virtual lUInt16 measureText(const lChar16 *text, int len, lUInt16 *widths, lUInt8 *flags, int max_width, @@ -60,9 +60,9 @@ class LBitmapFont : public LVBaseFont { /// returns italic flag virtual int getItalic() const; - //virtual bool getGlyphImage(lUInt16 code, lUInt8 *buf, lChar16 def_char = 0); + //virtual bool getGlyphImage(lUInt32 code, lUInt8 *buf, lChar16 def_char = 0); - virtual LVFontGlyphCacheItem *getGlyph(lUInt16 ch, lChar16 def_char = 0); + virtual LVFontGlyphCacheItem *getGlyph(lUInt32 ch, lChar16 def_char = 0); /// returns char width virtual int getCharWidth(lChar16 ch, lChar16 def_char = 0) { diff --git a/crengine/src/private/lvfontboldtransform.cpp b/crengine/src/private/lvfontboldtransform.cpp index edae8dabf0..aec67cf601 100644 --- a/crengine/src/private/lvfontboldtransform.cpp +++ b/crengine/src/private/lvfontboldtransform.cpp @@ -39,7 +39,7 @@ int LVFontBoldTransform::getHyphenWidth() { } bool -LVFontBoldTransform::getGlyphInfo(lUInt16 code, LVFont::glyph_info_t *glyph, lChar16 def_char) { +LVFontBoldTransform::getGlyphInfo(lUInt32 code, LVFont::glyph_info_t *glyph, lChar16 def_char) { bool res = _baseFont->getGlyphInfo(code, glyph, def_char); if (!res) return res; @@ -91,7 +91,7 @@ lUInt32 LVFontBoldTransform::getTextWidth(const lChar16 *text, int len) { return 0; } -LVFontGlyphCacheItem *LVFontBoldTransform::getGlyph(lUInt16 ch, lChar16 def_char) { +LVFontGlyphCacheItem *LVFontBoldTransform::getGlyph(lUInt32 ch, lChar16 def_char) { LVFontGlyphCacheItem *item = _glyph_cache.get(ch); if (item) diff --git a/crengine/src/private/lvfontboldtransform.h b/crengine/src/private/lvfontboldtransform.h index 9e63163546..df3dbe16fe 100644 --- a/crengine/src/private/lvfontboldtransform.h +++ b/crengine/src/private/lvfontboldtransform.h @@ -50,7 +50,7 @@ class LVFontBoldTransform : public LVFont { \param glyph is pointer to glyph_info_t struct to place retrieved info \return true if glyh was found */ - virtual bool getGlyphInfo(lUInt16 code, glyph_info_t *glyph, lChar16 def_char = 0); + virtual bool getGlyphInfo(lUInt32 code, glyph_info_t *glyph, lChar16 def_char = 0); /** \brief measure text \param text is text string pointer @@ -83,7 +83,7 @@ class LVFontBoldTransform : public LVFont { \param code is unicode character \return glyph pointer if glyph was found, NULL otherwise */ - virtual LVFontGlyphCacheItem *getGlyph(lUInt16 ch, lChar16 def_char = 0); + virtual LVFontGlyphCacheItem *getGlyph(lUInt32 ch, lChar16 def_char = 0); /** \brief get glyph image in 1 byte per pixel format \param code is unicode character diff --git a/crengine/src/private/lvfreetypeface.cpp b/crengine/src/private/lvfreetypeface.cpp index 70370c2f6f..e808fd8a0a 100644 --- a/crengine/src/private/lvfreetypeface.cpp +++ b/crengine/src/private/lvfreetypeface.cpp @@ -636,7 +636,7 @@ bool LVFreeTypeFace::hbCalcCharWidth(LVCharPosInfo *posInfo, const LVCharTriplet #endif // USE_HARFBUZZ==1 -FT_UInt LVFreeTypeFace::getCharIndex(lChar16 code, lChar16 def_char) { +FT_UInt LVFreeTypeFace::getCharIndex(lUInt32 code, lChar16 def_char) { if (code == '\t') code = ' '; FT_UInt ch_glyph_index = FT_Get_Char_Index(_face, code); @@ -650,7 +650,7 @@ FT_UInt LVFreeTypeFace::getCharIndex(lChar16 code, lChar16 def_char) { return ch_glyph_index; } -bool LVFreeTypeFace::getGlyphInfo(lUInt16 code, LVFont::glyph_info_t *glyph, lChar16 def_char) { +bool LVFreeTypeFace::getGlyphInfo(lUInt32 code, LVFont::glyph_info_t *glyph, lChar16 def_char) { //FONT_GUARD int glyph_index = getCharIndex(code, 0); if (glyph_index == 0) { @@ -1022,7 +1022,7 @@ void LVFreeTypeFace::updateTransform() { // } } -LVFontGlyphCacheItem *LVFreeTypeFace::getGlyph(lUInt16 ch, lChar16 def_char) { +LVFontGlyphCacheItem *LVFreeTypeFace::getGlyph(lUInt32 ch, lChar16 def_char) { //FONT_GUARD FT_UInt ch_glyph_index = getCharIndex(ch, 0); if (ch_glyph_index == 0) { diff --git a/crengine/src/private/lvfreetypeface.h b/crengine/src/private/lvfreetypeface.h index 3ab24d4d15..639ccde201 100644 --- a/crengine/src/private/lvfreetypeface.h +++ b/crengine/src/private/lvfreetypeface.h @@ -194,13 +194,11 @@ class LVFreeTypeFace : public LVFont { #endif // USE_HARFBUZZ==1 - FT_UInt getCharIndex(lChar16 code, lChar16 def_char); - /** \brief get glyph info \param glyph is pointer to glyph_info_t struct to place retrieved info \return true if glyh was found */ - virtual bool getGlyphInfo(lUInt16 code, glyph_info_t *glyph, lChar16 def_char = 0); + virtual bool getGlyphInfo(lUInt32 code, glyph_info_t *glyph, lChar16 def_char = 0); /* // USE GET_CHAR_FLAGS instead inline int calcCharFlags( lChar16 ch ) @@ -258,7 +256,7 @@ class LVFreeTypeFace : public LVFont { \param code is unicode character \return glyph pointer if glyph was found, NULL otherwise */ - virtual LVFontGlyphCacheItem *getGlyph(lUInt16 ch, lChar16 def_char = 0); + virtual LVFontGlyphCacheItem *getGlyph(lUInt32 ch, lChar16 def_char = 0); #if USE_HARFBUZZ == 1 @@ -340,6 +338,8 @@ class LVFreeTypeFace : public LVFont { } virtual void Clear(); +protected: + FT_UInt getCharIndex(lUInt32 code, lChar16 def_char); }; #endif // (USE_FREETYPE==1) diff --git a/crengine/src/private/lvwin32font.cpp b/crengine/src/private/lvwin32font.cpp index d15d516684..8a7fbe1f8e 100644 --- a/crengine/src/private/lvwin32font.cpp +++ b/crengine/src/private/lvwin32font.cpp @@ -118,7 +118,7 @@ bool LVBaseWin32Font::Create(int size, int weight, bool italic, css_font_family_ \param glyph is pointer to glyph_info_t struct to place retrieved info \return true if glyh was found */ -bool LVWin32DrawFont::getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char ) +bool LVWin32DrawFont::getGlyphInfo( lUInt32 code, glyph_info_t * glyph, lChar16 def_char ) { return false; } @@ -356,7 +356,7 @@ void LVWin32DrawFont::DrawTextString( LVDrawBuf * buf, int x, int y, \param buf is buffer [width*height] to place glyph data \return true if glyph was found */ -bool LVWin32DrawFont::getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char) +bool LVWin32DrawFont::getGlyphImage(lUInt32 code, lUInt8 * buf, lChar16 def_char) { return false; } @@ -505,7 +505,7 @@ glyph_t * LVWin32Font::GetGlyphRec( lChar16 ch ) \param glyph is pointer to glyph_info_t struct to place retrieved info \return true if glyh was found */ -bool LVWin32Font::getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char ) +bool LVWin32Font::getGlyphInfo( lUInt32 code, glyph_info_t * glyph, lChar16 def_char ) { if (_hfont==NULL) return false; @@ -618,7 +618,7 @@ lUInt16 LVWin32Font::measureText( \param buf is buffer [width*height] to place glyph data \return true if glyph was found */ -bool LVWin32Font::getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char) +bool LVWin32Font::getGlyphImage(lUInt32 code, lUInt8 * buf, lChar16 def_char) { if (_hfont==NULL) return false; diff --git a/crengine/src/private/lvwin32font.h b/crengine/src/private/lvwin32font.h index 65c1ec6e90..5a5e6f910c 100644 --- a/crengine/src/private/lvwin32font.h +++ b/crengine/src/private/lvwin32font.h @@ -104,7 +104,7 @@ class LVBaseWin32Font : public LVBaseFont return css_ff_inherit; } - virtual LVFontGlyphCacheItem * getGlyph(lUInt16 ch, lChar16 def_char=0) { + virtual LVFontGlyphCacheItem * getGlyph(lUInt32 ch, lChar16 def_char=0) { return NULL; } @@ -127,7 +127,7 @@ class LVWin32DrawFont : public LVBaseWin32Font \param glyph is pointer to glyph_info_t struct to place retrieved info \return true if glyh was found */ - virtual bool getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char=0 ); + virtual bool getGlyphInfo( lUInt32 code, glyph_info_t * glyph, lChar16 def_char=0 ); /** \brief measure text \param glyph is pointer to glyph_info_t struct to place retrieved info @@ -164,7 +164,7 @@ class LVWin32DrawFont : public LVBaseWin32Font \param buf is buffer [width*height] to place glyph data \return true if glyph was found */ - virtual bool getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0); + virtual bool getGlyphImage(lUInt32 code, lUInt8 * buf, lChar16 def_char=0); }; @@ -302,7 +302,7 @@ class LVWin32Font : public LVBaseWin32Font \param glyph is pointer to glyph_info_t struct to place retrieved info \return true if glyh was found */ - virtual bool getGlyphInfo( lUInt16 code, glyph_info_t * glyph, lChar16 def_char=0 ); + virtual bool getGlyphInfo( lUInt32 code, glyph_info_t * glyph, lChar16 def_char=0 ); /** \brief measure text \param glyph is pointer to glyph_info_t struct to place retrieved info @@ -331,7 +331,7 @@ class LVWin32Font : public LVBaseWin32Font \param buf is buffer [width*height] to place glyph data \return true if glyph was found */ - virtual bool getGlyphImage(lUInt16 code, lUInt8 * buf, lChar16 def_char=0); + virtual bool getGlyphImage(lUInt32 code, lUInt8 * buf, lChar16 def_char=0); virtual void Clear(); From cbc439070fae3409f7c3b7e311194856127c05bc Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Sun, 22 Dec 2019 20:10:48 +0400 Subject: [PATCH 5/8] Added separate functions UnicodeToWtf8() and Wtf8ToUnicode() to fix an old bug in Android that older than M (API23), which can't process 4-byte UTF-8 sequences. How to reproduce this bug: just open a text file with such UTF-8 sequences and you will get a crash with the following error: "input is not valid Modified UTF-8: illegal start byte 0xf0". Reason: unhandled exception: 'input is not valid Modified UTF-8: illegal start byte 0xf0'. stack trace (for commit a1bf2be3bcb19415640dae8becae83a6aba4fadd): _JNIEnv::NewStringUTF(char const*) // jni.h:841 jstring CRJNIEnv::toJavaString( const lString16 & str ) // cr3java.cpp:18 void CRStringField::set( const lString16& str) // cr3java.h:165 JNIEXPORT jobject JNICALL Java_org_coolreader_crengine_DocView_getPositionPropsInternal (JNIEnv * _env, jobject _this, jstring _path) // docview.cpp:1662 at org.coolreader.crengine.DocView.getPositionPropsInternal(Native Method) at org.coolreader.crengine.DocView.getPositionProps(DocView.java:270) at org.coolreader.crengine.ReaderView.preparePageImage(ReaderView.java:3248) at org.coolreader.crengine.ReaderView.access$1700(ReaderView.java:42) at org.coolreader.crengine.ReaderView$LoadDocumentTask.work(ReaderView.java:4829) at org.coolreader.crengine.Engine$TaskHandler.run(Engine.java:180) at android.os.Handler.handleCallback(Handler.java:725) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:137) at org.coolreader.crengine.BackgroundThread.run(BackgroundThread.java:122) And solution: just don't pass such strings to env->NewStringUTF(), but pass WTF-8 strings. But this don't fix the crash when CoolReader finds files whose names contain such characters! --- android/jni/cr3engine.cpp | 12 +- android/jni/cr3java.cpp | 19 +- android/jni/cr3java.h | 1 + android/jni/org_coolreader_crengine_Engine.h | 6 +- .../src/org/coolreader/crengine/Engine.java | 6 +- crengine/Tools/CMakeLists.txt | 1 + crengine/Tools/wtf8-test/CMakeLists.txt | 26 ++ crengine/Tools/wtf8-test/main.cpp | 42 ++++ crengine/include/lvstring.h | 10 + crengine/src/lvstring.cpp | 226 +++++++++++++++++- 10 files changed, 335 insertions(+), 14 deletions(-) create mode 100644 crengine/Tools/wtf8-test/CMakeLists.txt create mode 100644 crengine/Tools/wtf8-test/main.cpp diff --git a/android/jni/cr3engine.cpp b/android/jni/cr3engine.cpp index 4e74a40901..6dc2f70d0e 100644 --- a/android/jni/cr3engine.cpp +++ b/android/jni/cr3engine.cpp @@ -552,7 +552,9 @@ void cr3androidFatalErrorHandler(int errorCode, const char * errorText ) /// set fatal error handler void crSetFatalErrorHandler( lv_FatalErrorHandler_t * handler ); -jboolean initInternal(JNIEnv * penv, jclass obj, jobjectArray fontArray) { +jboolean initInternal(JNIEnv * penv, jclass obj, jobjectArray fontArray, jint sdk_int) { + + CRJNIEnv::sdk_int = sdk_int; CRJNIEnv env(penv); @@ -591,13 +593,13 @@ jboolean initInternal(JNIEnv * penv, jclass obj, jobjectArray fontArray) { /* * Class: org_coolreader_crengine_Engine * Method: initInternal - * Signature: ([Ljava/lang/String;)Z + * Signature: ([Ljava/lang/String;I)Z */ JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_initInternal - (JNIEnv * penv, jclass obj, jobjectArray fontArray) + (JNIEnv * penv, jclass obj, jobjectArray fontArray, jint sdk_int) { jboolean res = JNI_FALSE; - COFFEE_TRY_JNI(penv, res = initInternal(penv, obj, fontArray)); + COFFEE_TRY_JNI(penv, res = initInternal(penv, obj, fontArray, sdk_int)); return res; } @@ -775,7 +777,7 @@ JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_setKeyBacklightIn static JNINativeMethod sEngineMethods[] = { /* name, signature, funcPtr */ - {"initInternal", "([Ljava/lang/String;)Z", (void*)Java_org_coolreader_crengine_Engine_initInternal}, + {"initInternal", "([Ljava/lang/String;I)Z", (void*)Java_org_coolreader_crengine_Engine_initInternal}, {"uninitInternal", "()V", (void*)Java_org_coolreader_crengine_Engine_uninitInternal}, {"getFontFaceListInternal", "()[Ljava/lang/String;", (void*)Java_org_coolreader_crengine_Engine_getFontFaceListInternal}, {"setCacheDirectoryInternal", "(Ljava/lang/String;I)Z", (void*)Java_org_coolreader_crengine_Engine_setCacheDirectoryInternal}, diff --git a/android/jni/cr3java.cpp b/android/jni/cr3java.cpp index aea3e678f1..cc96a0ebb5 100644 --- a/android/jni/cr3java.cpp +++ b/android/jni/cr3java.cpp @@ -2,20 +2,35 @@ #include +// M is for Marshmallow! +#define ANDROID_SDK_M 23 + +uint8_t CRJNIEnv::sdk_int = 0; + lString16 CRJNIEnv::fromJavaString( jstring str ) { if (!str) return lString16::empty_str; jboolean iscopy; const char * s = env->GetStringUTFChars(str, &iscopy); - lString16 res(s); + lString16 res; + if (CRJNIEnv::sdk_int >= ANDROID_SDK_M) + res = Utf8ToUnicode(s); + else + res = Wtf8ToUnicode(s); env->ReleaseStringUTFChars(str, s); return res; } jstring CRJNIEnv::toJavaString( const lString16 & str ) { - return env->NewStringUTF(UnicodeToUtf8(str).c_str()); + if (CRJNIEnv::sdk_int >= ANDROID_SDK_M) + return env->NewStringUTF(UnicodeToUtf8(str).c_str()); + // To support 4-byte UTF-8 sequence on Android older that 6.0 (API 23), + // we encode characters with codes >= 0x10000 to WTF-8. + // Otherwise, we have crash with following message: + // "input is not valid Modified UTF-8: illegal start byte 0xf0" + return env->NewStringUTF(UnicodeToWtf8(str).c_str()); } void CRJNIEnv::fromJavaStringArray( jobjectArray array, lString16Collection & dst ) diff --git a/android/jni/cr3java.h b/android/jni/cr3java.h index ce03add21b..43029b0bf8 100644 --- a/android/jni/cr3java.h +++ b/android/jni/cr3java.h @@ -58,6 +58,7 @@ class BitmapAccessorInterface { class CRJNIEnv { public: JNIEnv * env; + static uint8_t sdk_int; CRJNIEnv(JNIEnv * pEnv) : env(pEnv) { } JNIEnv * operator -> () { return env; } lString16 fromJavaString( jstring str ); diff --git a/android/jni/org_coolreader_crengine_Engine.h b/android/jni/org_coolreader_crengine_Engine.h index c294706fda..559bc5a9c2 100644 --- a/android/jni/org_coolreader_crengine_Engine.h +++ b/android/jni/org_coolreader_crengine_Engine.h @@ -22,10 +22,10 @@ extern "C" { /* * Class: org_coolreader_crengine_Engine * Method: initInternal - * Signature: ([Ljava/lang/String;)Z + * Signature: ([Ljava/lang/String;I)Z */ JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_initInternal - (JNIEnv *, jclass, jobjectArray); + (JNIEnv *, jclass, jobjectArray, jint sdk_int); /* * Class: org_coolreader_crengine_Engine @@ -113,7 +113,7 @@ JNIEXPORT void JNICALL Java_org_coolreader_crengine_Engine_suspendLongOperationI * Signature: (Ljava/lang/String;)Z */ JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_haveFcLangCodeInternal - (JNIEnv *, jclass, jstring); + (JNIEnv *, jclass, jstring); /* * Class: org_coolreader_crengine_Engine diff --git a/android/src/org/coolreader/crengine/Engine.java b/android/src/org/coolreader/crengine/Engine.java index 0eacf9a56b..9786a87fc9 100644 --- a/android/src/org/coolreader/crengine/Engine.java +++ b/android/src/org/coolreader/crengine/Engine.java @@ -590,7 +590,7 @@ public void initAgain() { } mFonts = findFonts(); findExternalHyphDictionaries(); - if (!initInternal(mFonts)) { + if (!initInternal(mFonts, DeviceInfo.getSDKLevel())) { log.i("Engine.initInternal failed!"); throw new RuntimeException("Cannot initialize CREngine JNI"); } @@ -599,7 +599,7 @@ public void initAgain() { } // Native functions - private native static boolean initInternal(String[] fontList); + private native static boolean initInternal(String[] fontList, int sdk_int); private native static void uninitInternal(); @@ -2022,7 +2022,7 @@ public MountPathCorrector getPathCorrector() { } mFonts = findFonts(); findExternalHyphDictionaries(); - if (!initInternal(mFonts)) { + if (!initInternal(mFonts, DeviceInfo.getSDKLevel())) { log.i("Engine.initInternal failed!"); throw new RuntimeException("Cannot initialize CREngine JNI"); } diff --git a/crengine/Tools/CMakeLists.txt b/crengine/Tools/CMakeLists.txt index f70201d188..cbc0694b47 100644 --- a/crengine/Tools/CMakeLists.txt +++ b/crengine/Tools/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory(HyphConv) add_subdirectory(langstat) add_subdirectory(langstat2) add_subdirectory(glyphcache_bench) +add_subdirectory(wtf8-test) diff --git a/crengine/Tools/wtf8-test/CMakeLists.txt b/crengine/Tools/wtf8-test/CMakeLists.txt new file mode 100644 index 0000000000..e17ae5c89c --- /dev/null +++ b/crengine/Tools/wtf8-test/CMakeLists.txt @@ -0,0 +1,26 @@ + +set(SRC_LIST + main.cpp +) + +set(crengine_part_SRC_LIST + ../../src/cp_stats.cpp + ../../src/crtxtenc.cpp + ../../src/lvmemman.cpp + ../../src/lvstream.cpp + ../../src/lvstring.cpp +) + +#add_definitions(-DBUILD_LITE=1) + +if(UNIX) + add_definitions(-DLINUX -D_LINUX) +endif(UNIX) + +if(WIN32) + add_definitions(-DWIN32 -D_CONSOLE) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -mconsole") +endif(WIN32) + +add_executable(wtf8-test ${SRC_LIST} ${crengine_part_SRC_LIST}) +target_link_libraries(wtf8-test ${STD_LIBS}) diff --git a/crengine/Tools/wtf8-test/main.cpp b/crengine/Tools/wtf8-test/main.cpp new file mode 100644 index 0000000000..4c176a85b8 --- /dev/null +++ b/crengine/Tools/wtf8-test/main.cpp @@ -0,0 +1,42 @@ +#include "lvstring.h" + +#include + +lUInt32 uni_chars[] = { + 0x10000, // LINEAR B SYLLABLE B008 A + 0x10123, // AEGEAN NUMBER TWO THOUSAND + 0x10081, // LINEAR B IDEOGRAM B102 WOMAN + 0x1F600, // GRINNING FACE + 0x1F601, // GRINNING FACE WITH SMILING EYES + 0x1F602, // FACE WITH TEARS OF JOY + 0x1F603, // SMILING FACE WITH OPEN MOUTH + 0x1F604, // SMILING FACE WITH OPEN MOUTH AND SMILING EYES + 0x1F605, // SMILING FACE WITH OPEN MOUTH AND COLD SWEAT + 0x1F606, // SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES + 0x1F607, // SMILING FACE WITH HALO + 0x1F608, // SMILING FACE WITH HORNS + 0x1F609, // WINKING FACE + 0x1F60A, // SMILING FACE WITH SMILING EYES + 0x1F60B // FACE SAVOURING DELICIOUS FOOD +}; + +int main(int argc, char* argv[]) +{ + lString16 src; + for (size_t i = 0; i < sizeof(uni_chars)/sizeof(lUInt32); i++) + { + src.append(1, uni_chars[i]); + } + lString8 dst = UnicodeToUtf8(src); + printf("UTF8: %s\n", dst.c_str()); + lString8 dstw = UnicodeToWtf8(src); + printf("WTF8: %s\n", dstw.c_str()); + // Back to unicode + lString16 str2 = Wtf8ToUnicode(dstw); + // and compare... + if (str2.compare(src) == 0) + printf("OK, strings is equal.\n"); + else + printf("Sorry, strings is NOT equal!\n"); + return 0; +} diff --git a/crengine/include/lvstring.h b/crengine/include/lvstring.h index 106cc6e182..e34ff95ce2 100644 --- a/crengine/include/lvstring.h +++ b/crengine/include/lvstring.h @@ -1030,6 +1030,10 @@ lString8 UnicodeToLocal( const lString16 & str ); lString8 UnicodeToUtf8( const lString16 & str ); /// converts wide unicode string to utf-8 string lString8 UnicodeToUtf8(const lChar16 * s, int count); +/// converts wide unicode string to wtf-8 string +lString8 UnicodeToWtf8( const lString16 & str ); +/// converts wide unicode string to wtf-8 string +lString8 UnicodeToWtf8(const lChar16 * s, int count); /// converts unicode string to 8-bit string using specified conversion table lString8 UnicodeTo8Bit( const lString16 & str, const lChar8 * * table ); /// converts 8-bit string to unicode string using specified conversion table for upper 128 characters @@ -1044,6 +1048,12 @@ lString16 Utf8ToUnicode( const char * s ); lString16 Utf8ToUnicode( const char * s, int sz ); /// converts utf-8 string fragment to wide unicode string void Utf8ToUnicode(const lUInt8 * src, int &srclen, lChar16 * dst, int &dstlen); +/// converts wtf-8 string to wide unicode string +lString16 Wtf8ToUnicode( const lString8 & str ); +/// converts utf-8 c-string to wide unicode string +lString16 Wtf8ToUnicode( const char * s ); +/// converts utf-8 string fragment to wide unicode string +lString16 Wtf8ToUnicode( const char * s, int sz ); /// decodes path like "file%20name" to "file name" lString16 DecodeHTMLUrlString( lString16 s ); /// truncates string by specified size, appends ... if truncated, prefers to wrap whole words diff --git a/crengine/src/lvstring.cpp b/crengine/src/lvstring.cpp index 17053d254c..54a92e12e1 100644 --- a/crengine/src/lvstring.cpp +++ b/crengine/src/lvstring.cpp @@ -2842,7 +2842,78 @@ int Utf8CharCount( const lChar8 * str, int len ) return count; } -inline int charUtf8ByteCount(int ch) { +int Wtf8CharCount( const lChar8 * str ) +{ + int count = 0; + lUInt8 ch; + while ( (ch=*str++) ) { + if ( (ch & 0x80) == 0 ) { + } else if ( (ch & 0xE0) == 0xC0 ) { + if ( !(*str++) ) + break; + } else if ( (ch & 0xF0) == 0xE0 ) { + if ( !(*str++) ) + break; + if ( !(*str++) ) + break; + if ( (ch & 0xF0) == 0xE0 ) { + if ( !(*str++) ) + break; + if ( !(*str++) ) + break; + if ( !(*str++) ) + break; + } + } else if ( (ch & 0xF8) == 0xF0 ) { + // Mostly unused + if ( !(*str++) ) + break; + if ( !(*str++) ) + break; + if ( !(*str++) ) + break; + } else { + // invalid first byte in UTF-8 sequence, just leave as is + ; + } + count++; + } + return count; +} + +int Wtf8CharCount( const lChar8 * str, int len ) +{ + if (len == 0) + return 0; + int count = 0; + lUInt8 ch; + const lChar8 * endp = str + len; + while ((ch=*str)) { + if ( (ch & 0x80) == 0 ) { + str++; + } else if ( (ch & 0xE0) == 0xC0 ) { + str+=2; + } else if ( (ch & 0xF0) == 0xE0 ) { + str+=3; + ch=*str; + if ( (ch & 0xF0) == 0xE0 ) { + str+=3; + } + } else if ( (ch & 0xF8) == 0xF0 ) { + // Mostly unused + str+=4; + } else { + // invalid first byte of UTF-8 sequence, just leave as is + str++; + } + if (str > endp) + break; + count++; + } + return count; +} + +inline int charUtf8ByteCount(lUInt32 ch) { if (!(ch & ~0x7F)) return 1; if (!(ch & ~0x7FF)) @@ -2866,6 +2937,18 @@ int Utf8ByteCount(const lChar16 * str) return count; } +inline int charWtf8ByteCount(lUInt32 ch) { + if (!(ch & ~0x7F)) + return 1; + if (!(ch & ~0x7FF)) + return 2; + if (!(ch & ~0xFFFF)) + return 3; + if (!(ch & ~0x1FFFFF)) + return 6; + return 1; +} + int Utf8ByteCount(const lChar16 * str, int len) { int count = 0; @@ -2877,6 +2960,17 @@ int Utf8ByteCount(const lChar16 * str, int len) return count; } +int Wtf8ByteCount(const lChar16 * str, int len) +{ + int count = 0; + lUInt32 ch; + while ((len--) > 0) { + ch = *str++; + count += charWtf8ByteCount(ch); + } + return count; +} + lString16 Utf8ToUnicode( const lString8 & str ) { return Utf8ToUnicode( str.c_str() ); @@ -2915,6 +3009,52 @@ static void DecodeUtf8(const char * s, lChar16 * p, int len) } } +static void DecodeWtf8(const char * s, lChar16 * p, int len) +{ + lChar16 * endp = p + len; + lUInt32 ch; + while (p < endp) { + ch = *s; + if ( (ch & 0x80) == 0 ) { + *p++ = (char)ch; + s++; + } else if ( (ch & 0xE0) == 0xC0 ) { + *p++ = ((ch & 0x1F) << 6) + | CONT_BYTE(1,0); + s += 2; + } else if ( (ch & 0xF0) == 0xE0 ) { + *p++ = ((ch & 0x0F) << 12) + | CONT_BYTE(1,6) + | CONT_BYTE(2,0); + s += 3; + if (*(p-1) >= 0xD800 && *(p-1) <= 0xDBFF) { // what we wrote is a high surrogate, + lUInt32 next = *s; // and there's room next for a low surrogate + if ( (next & 0xF0) == 0xE0) { // is a 3-bytes sequence + next = ((next & 0x0F) << 12) | CONT_BYTE(1,6) | CONT_BYTE(2,0); + if (next >= 0xDC00 && next <= 0xDFFF) { // is a low surrogate: valid surrogates sequence + ch = 0x10000 + ((*(p-1) & 0x3FF)<<10) + (next & 0x3FF); + p--; // rewind to override what we wrote + *p++ = ch; + s += 3; + } + } + } + } else if ( (ch & 0xF8) == 0xF0 ) { + // Mostly unused + *p++ = ((ch & 0x07) << 18) + | CONT_BYTE(1,12) + | CONT_BYTE(2,6) + | CONT_BYTE(3,0); + s += 4; + } else { + // Invalid first byte in UTF-8 sequence + // Pass with mask 0x7F, to resolve exception around env->NewStringUTF() + *p++ = (char) (ch & 0x7F); + s++; + } + } +} + // Top two bits are 10, i.e. original & 11000000(2) == 10000000(2) #define IS_FOLLOWING(index) ((s[index] & 0xC0) == 0x80) @@ -3035,6 +3175,36 @@ lString16 Utf8ToUnicode( const char * s, int sz ) { return dst; } +lString16 Wtf8ToUnicode( const lString8 & str ) +{ + return Wtf8ToUnicode( str.c_str() ); +} + +lString16 Wtf8ToUnicode( const char * s ) { + if (!s || !s[0]) + return lString16::empty_str; + int len = Wtf8CharCount( s ); + if (!len) + return lString16::empty_str; + lString16 dst; + dst.append(len, (lChar16)0); + lChar16 * p = dst.modify(); + DecodeWtf8(s, p, len); + return dst; +} + +lString16 Wtf8ToUnicode( const char * s, int sz ) { + if (!s || !s[0] || sz <= 0) + return lString16::empty_str; + int len = Utf8CharCount( s, sz ); + if (!len) + return lString16::empty_str; + lString16 dst; + dst.append(len, 0); + lChar16 * p = dst.modify(); + DecodeWtf8(s, p, len); + return dst; +} lString8 UnicodeToUtf8(const lChar16 * s, int count) { @@ -3079,6 +3249,60 @@ lString8 UnicodeToUtf8( const lString16 & str ) return UnicodeToUtf8(str.c_str(), str.length()); } +lString8 UnicodeToWtf8(const lChar16 * s, int count) +{ + if (count <= 0) + return lString8::empty_str; + lString8 dst; + int len = Wtf8ByteCount(s, count); + if (len <= 0) + return lString8::empty_str; + dst.append( len, ' ' ); + lChar8 * buf = dst.modify(); + { + lUInt32 ch; + while ((count--) > 0) { + ch = *s++; + if (!(ch & ~0x7F)) { + *buf++ = ( (lUInt8)ch ); + } else if (!(ch & ~0x7FF)) { + *buf++ = ( (lUInt8) ( ((ch >> 6) & 0x1F) | 0xC0 ) ); + *buf++ = ( (lUInt8) ( ((ch ) & 0x3F) | 0x80 ) ); + } else if (!(ch & ~0xFFFF)) { + *buf++ = ( (lUInt8) ( ((ch >> 12) & 0x0F) | 0xE0 ) ); + *buf++ = ( (lUInt8) ( ((ch >> 6) & 0x3F) | 0x80 ) ); + *buf++ = ( (lUInt8) ( ((ch ) & 0x3F) | 0x80 ) ); + } else if (!(ch & ~0x1FFFFF)) { + // UTF-16 Scalar Value + // 000uuuuu xxxxxxxxxxxxxxxx + // UTF-16 + // 110110wwwwxxxxxx 110111xxxxxxxxxx + // wwww = uuuuu - 1 + lUInt16 wwww = (ch >> 16) - 1; + lUInt16 low = ch & 0xFFFF; + lUInt32 hiSurr = 0xD800 | (wwww << 6) | (low >> 10); // high surrogate + lUInt32 lowSurr = 0xDC00 | (low & 0x3FF); // low surrogate + *buf++ = ( (lUInt8) ( ((hiSurr >> 12) & 0x0F) | 0xE0 ) ); + *buf++ = ( (lUInt8) ( ((hiSurr >> 6) & 0x3F) | 0x80 ) ); + *buf++ = ( (lUInt8) ( ((hiSurr ) & 0x3F) | 0x80 ) ); + *buf++ = ( (lUInt8) ( ((lowSurr >> 12) & 0x0F) | 0xE0 ) ); + *buf++ = ( (lUInt8) ( ((lowSurr >> 6) & 0x3F) | 0x80 ) ); + *buf++ = ( (lUInt8) ( ((lowSurr ) & 0x3F) | 0x80 ) ); + } else { + // invalid codepoint + // In Unicode Standard codepoint must be in range U+0000 .. U+10FFFF + *buf++ = '?'; + } + } + } + return dst; +} + +lString8 UnicodeToWtf8( const lString16 & str ) +{ + return UnicodeToWtf8(str.c_str(), str.length()); +} + lString8 UnicodeTo8Bit( const lString16 & str, const lChar8 * * table ) { lString8 buf; From 37a4b0fed34b50b46e5c627d19a336aa28eace25 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Wed, 25 Dec 2019 00:24:33 +0400 Subject: [PATCH 6/8] Continue of refactoring: files lvstring.{h,cpp} splitted to the next files with corresponding classess: crlog.{h,cpp} serialbuf.{h,cpp} lvstring8collection.{h,cpp} lvstring16collection.{h,cpp} lvstring16hashedcollection.{h,cpp}. From file lvtypes.h extracted class CRTimerUtil (into file crtimerutil.h). --- android/app/CMakeLists.txt | 5 + android/jni/Android.mk | 5 + android/jni/cr3java.cpp | 1 + android/jni/gen_jni_studio | 2 +- crengine/CMakeLists.txt | 113 ++- crengine/Tools/HyphConv/CMakeLists.txt | 2 + crengine/Tools/langstat/CMakeLists.txt | 2 + crengine/Tools/langstat2/CMakeLists.txt | 2 + crengine/Tools/wtf8-test/CMakeLists.txt | 2 + crengine/include/crlog.h | 68 ++ crengine/include/crskin.h | 1 + crengine/include/crtest.h | 1 + crengine/include/crtimerutil.h | 93 ++ crengine/include/lstridmap.h | 1 + crengine/include/lvembeddedfont.h | 1 + crengine/include/lvfntman.h | 1 + crengine/include/lvpagesplitter.h | 3 + crengine/include/lvstream.h | 1 + crengine/include/lvstring.h | 403 +------- crengine/include/lvstring16collection.h | 74 ++ crengine/include/lvstring16hashedcollection.h | 48 + crengine/include/lvstring8collection.h | 74 ++ crengine/include/lvstyles.h | 3 + crengine/include/lvtinydom.h | 2 + crengine/include/lvtypes.h | 78 -- crengine/include/props.h | 1 + crengine/include/serialbuf.h | 118 +++ crengine/obsolete/lvstringbuf16.h | 58 ++ crengine/obsolete/lvstringbuf8.h | 52 + crengine/obsolete/readme.txt | 1 + crengine/src/chmfmt.cpp | 1 + crengine/src/crconcurrent.cpp | 1 + crengine/src/cri18n.cpp | 2 + crengine/src/crlog.cpp | 257 +++++ crengine/src/crtxtenc.cpp | 1 + crengine/src/docxfmt.cpp | 1 + crengine/src/epubfmt.cpp | 1 + crengine/src/fb3fmt.cpp | 1 + crengine/src/hist.cpp | 1 + crengine/src/hyphman.cpp | 2 + crengine/src/lvdrawbuf.cpp | 1 + crengine/src/lvembeddedfont.cpp | 1 + crengine/src/lvfntman.cpp | 1 + crengine/src/lvimg.cpp | 1 + crengine/src/lvmemman.cpp | 1 + crengine/src/lvopc.cpp | 1 + crengine/src/lvpagesplitter.cpp | 2 + crengine/src/lvrend.cpp | 1 + crengine/src/lvstream.cpp | 1 + crengine/src/lvstring.cpp | 947 +----------------- crengine/src/lvstring16collection.cpp | 155 +++ crengine/src/lvstring16hashedcollection.cpp | 180 ++++ crengine/src/lvstring8collection.cpp | 83 ++ crengine/src/lvstyles.cpp | 1 + crengine/src/lvtinydom.cpp | 1 + crengine/src/pdbfmt.cpp | 1 + crengine/src/private/dumpfile.h | 40 + crengine/src/private/lvfontcache.cpp | 1 + crengine/src/private/lvfontcache.h | 1 + crengine/src/private/lvfreetypeface.cpp | 1 + crengine/src/private/lvfreetypefontman.cpp | 3 +- crengine/src/props.cpp | 1 + crengine/src/serialbuf.cpp | 337 +++++++ crengine/src/txtselector.cpp | 1 + crengine/src/wolutil.cpp | 2 +- crengine/src/wordfmt.cpp | 1 + 66 files changed, 1774 insertions(+), 1476 deletions(-) create mode 100644 crengine/include/crlog.h create mode 100644 crengine/include/crtimerutil.h create mode 100644 crengine/include/lvstring16collection.h create mode 100644 crengine/include/lvstring16hashedcollection.h create mode 100644 crengine/include/lvstring8collection.h create mode 100644 crengine/include/serialbuf.h create mode 100644 crengine/obsolete/lvstringbuf16.h create mode 100644 crengine/obsolete/lvstringbuf8.h create mode 100644 crengine/obsolete/readme.txt create mode 100644 crengine/src/crlog.cpp create mode 100644 crengine/src/lvstring16collection.cpp create mode 100644 crengine/src/lvstring16hashedcollection.cpp create mode 100644 crengine/src/lvstring8collection.cpp create mode 100644 crengine/src/private/dumpfile.h create mode 100644 crengine/src/serialbuf.cpp diff --git a/android/app/CMakeLists.txt b/android/app/CMakeLists.txt index e0cd5319ac..65d0b69e5a 100644 --- a/android/app/CMakeLists.txt +++ b/android/app/CMakeLists.txt @@ -44,6 +44,11 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_C_FLAGS_RELWITHDEBINFO}) set(CRENGINE_SRC_FILES ${CR3_ROOT}/crengine/src/cp_stats.cpp ${CR3_ROOT}/crengine/src/lvstring.cpp + ${CR3_ROOT}/crengine/src/lvstring8collection.cpp + ${CR3_ROOT}/crengine/src/lvstring16collection.cpp + ${CR3_ROOT}/crengine/src/lvstring16hashedcollection.cpp + ${CR3_ROOT}/crengine/src/crlog.cpp + ${CR3_ROOT}/crengine/src/serialbuf.cpp ${CR3_ROOT}/crengine/src/props.cpp ${CR3_ROOT}/crengine/src/lstridmap.cpp ${CR3_ROOT}/crengine/src/rtfimp.cpp diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 99ddcdfb14..c8c57f6a79 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -40,6 +40,11 @@ LOCAL_CFLAGS += -g -O1 -fexceptions -flto CRENGINE_SRC_FILES := \ ../../crengine/src/cp_stats.cpp \ ../../crengine/src/lvstring.cpp \ + ../../crengine/src/lvstring8collection.cpp \ + ../../crengine/src/lvstring16collection.cpp \ + ../../crengine/src/lvstring16hashedcollection.cpp \ + ../../crengine/src/crlog.cpp \ + ../../crengine/src/serialbuf.cpp \ ../../crengine/src/props.cpp \ ../../crengine/src/lstridmap.cpp \ ../../crengine/src/rtfimp.cpp \ diff --git a/android/jni/cr3java.cpp b/android/jni/cr3java.cpp index cc96a0ebb5..35776e91f4 100644 --- a/android/jni/cr3java.cpp +++ b/android/jni/cr3java.cpp @@ -1,4 +1,5 @@ #include "cr3java.h" +#include "../../crengine/include/crlog.h" #include diff --git a/android/jni/gen_jni_studio b/android/jni/gen_jni_studio index 6c61c78028..2d17c5c239 100755 --- a/android/jni/gen_jni_studio +++ b/android/jni/gen_jni_studio @@ -1,7 +1,7 @@ #!/bin/sh sdk="/opt/android-sdk-update-manager" -binclass_path=../app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes +binclass_path=../app/build/intermediates/javac/debug/classes export CLASSPATH="${CLASSPATH}:${sdk}/platforms/android-17/*:${binclass_path}" diff --git a/crengine/CMakeLists.txt b/crengine/CMakeLists.txt index f585ccb065..c9a3e192f8 100644 --- a/crengine/CMakeLists.txt +++ b/crengine/CMakeLists.txt @@ -3,64 +3,69 @@ option(BUILD_TOOLS "Build tools" OFF) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/fc-lang) -SET (CRENGINE_SOURCES -src/cp_stats.cpp -src/lvstring.cpp -src/props.cpp -src/lstridmap.cpp -src/rtfimp.cpp -src/cri18n.cpp -src/lvmemman.cpp -src/lvstyles.cpp -src/crtxtenc.cpp -src/lvtinydom.cpp -src/lvstream.cpp -src/lvxml.cpp -src/lvstsheet.cpp -src/txtselector.cpp -#src/xutils.cpp -src/crtest.cpp -fc-lang/fc-lang-cat.c +SET (CRENGINE_SOURCES + src/cp_stats.cpp + src/lvstring.cpp + src/lvstring8collection.cpp + src/lvstring16collection.cpp + src/lvstring16hashedcollection.cpp + src/serialbuf.cpp + src/crlog.cpp + src/props.cpp + src/lstridmap.cpp + src/rtfimp.cpp + src/cri18n.cpp + src/lvmemman.cpp + src/lvstyles.cpp + src/crtxtenc.cpp + src/lvtinydom.cpp + src/lvstream.cpp + src/lvxml.cpp + src/lvstsheet.cpp + src/txtselector.cpp + #src/xutils.cpp + src/crtest.cpp + fc-lang/fc-lang-cat.c ) if ( NOT ${GUI} STREQUAL FB2PROPS ) SET (CRENGINE_SOURCES ${CRENGINE_SOURCES} - src/lvbmpbuf.cpp - src/lvfnt.cpp - src/hyphman.cpp - src/lvembeddedfont.cpp - src/lvfont.cpp - src/lvfntman.cpp - src/crgui.cpp - src/lvimg.cpp - src/crskin.cpp - src/lvdrawbuf.cpp - src/lvdocview.cpp - src/lvpagesplitter.cpp - src/lvtextfm.cpp - src/lvrend.cpp - src/wolutil.cpp - src/hist.cpp - src/chmfmt.cpp - src/epubfmt.cpp - src/pdbfmt.cpp - src/wordfmt.cpp - src/lvopc.cpp - src/docxfmt.cpp - src/fb3fmt.cpp - src/crconcurrent.cpp - src/private/lvbasefont.cpp - src/private/lvbitmapfont.cpp - src/private/lvbitmapfontman.cpp - src/private/lvfontglyphcache.cpp - src/private/lvfontcache.cpp - src/private/lvfontboldtransform.cpp - src/private/lvfontdef.cpp - src/private/lvwin32font.cpp - src/private/lvwin32fontman.cpp - src/private/lvfreetypeface.cpp - src/private/lvfreetypefontman.cpp - #src/xutils.cpp + src/lvbmpbuf.cpp + src/lvfnt.cpp + src/hyphman.cpp + src/lvembeddedfont.cpp + src/lvfont.cpp + src/lvfntman.cpp + src/crgui.cpp + src/lvimg.cpp + src/crskin.cpp + src/lvdrawbuf.cpp + src/lvdocview.cpp + src/lvpagesplitter.cpp + src/lvtextfm.cpp + src/lvrend.cpp + src/wolutil.cpp + src/hist.cpp + src/chmfmt.cpp + src/epubfmt.cpp + src/pdbfmt.cpp + src/wordfmt.cpp + src/lvopc.cpp + src/docxfmt.cpp + src/fb3fmt.cpp + src/crconcurrent.cpp + src/private/lvbasefont.cpp + src/private/lvbitmapfont.cpp + src/private/lvbitmapfontman.cpp + src/private/lvfontglyphcache.cpp + src/private/lvfontcache.cpp + src/private/lvfontboldtransform.cpp + src/private/lvfontdef.cpp + src/private/lvwin32font.cpp + src/private/lvwin32fontman.cpp + src/private/lvfreetypeface.cpp + src/private/lvfreetypefontman.cpp + #src/xutils.cpp ) endif (NOT ${GUI} STREQUAL FB2PROPS) diff --git a/crengine/Tools/HyphConv/CMakeLists.txt b/crengine/Tools/HyphConv/CMakeLists.txt index 458c4571d1..8875b8d6d3 100644 --- a/crengine/Tools/HyphConv/CMakeLists.txt +++ b/crengine/Tools/HyphConv/CMakeLists.txt @@ -9,6 +9,8 @@ set(crengine_part_SRC_LIST ../../src/lvmemman.cpp ../../src/cp_stats.cpp ../../src/lvstream.cpp + ../../src/crlog.cpp + ../../src/serialbuf.cpp ) if(UNIX) diff --git a/crengine/Tools/langstat/CMakeLists.txt b/crengine/Tools/langstat/CMakeLists.txt index e38f18d492..d39056bc8d 100644 --- a/crengine/Tools/langstat/CMakeLists.txt +++ b/crengine/Tools/langstat/CMakeLists.txt @@ -9,6 +9,8 @@ set(crengine_part_SRC_LIST ../../src/lvmemman.cpp ../../src/lvstream.cpp ../../src/lvstring.cpp + ../../src/crlog.cpp + ../../src/serialbuf.cpp ) add_definitions(-DBUILD_LITE=1) diff --git a/crengine/Tools/langstat2/CMakeLists.txt b/crengine/Tools/langstat2/CMakeLists.txt index 4defad6ce4..ef27fb467c 100644 --- a/crengine/Tools/langstat2/CMakeLists.txt +++ b/crengine/Tools/langstat2/CMakeLists.txt @@ -9,6 +9,8 @@ set(crengine_part_SRC_LIST ../../src/lvmemman.cpp ../../src/lvstream.cpp ../../src/lvstring.cpp + ../../src/crlog.cpp + ../../src/serialbuf.cpp ) if(UNIX) diff --git a/crengine/Tools/wtf8-test/CMakeLists.txt b/crengine/Tools/wtf8-test/CMakeLists.txt index e17ae5c89c..00d8946607 100644 --- a/crengine/Tools/wtf8-test/CMakeLists.txt +++ b/crengine/Tools/wtf8-test/CMakeLists.txt @@ -9,6 +9,8 @@ set(crengine_part_SRC_LIST ../../src/lvmemman.cpp ../../src/lvstream.cpp ../../src/lvstring.cpp + ../../src/crlog.cpp + ../../src/serialbuf.cpp ) #add_definitions(-DBUILD_LITE=1) diff --git a/crengine/include/crlog.h b/crengine/include/crlog.h new file mode 100644 index 0000000000..f80ed83cda --- /dev/null +++ b/crengine/include/crlog.h @@ -0,0 +1,68 @@ +/** \file crlog.h + \brief logging class interface + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. +*/ + +#ifndef __CR_LOG_H_INCLUDED__ +#define __CR_LOG_H_INCLUDED__ + +#include + +/// Logger +class CRLog +{ +public: + /// log levels + enum log_level { + LL_FATAL, + LL_ERROR, + LL_WARN, + LL_INFO, + LL_DEBUG, + LL_TRACE + }; + /// set current log level + static void setLogLevel( log_level level ); + /// returns current log level + static log_level getLogLevel(); + /// returns true if specified log level is enabled + static bool isLogLevelEnabled( log_level level ); + /// returns true if log level is DEBUG or lower + static bool inline isDebugEnabled() { return isLogLevelEnabled( LL_DEBUG ); } + /// returns true if log level is TRACE + static bool inline isTraceEnabled() { return isLogLevelEnabled( LL_TRACE ); } + /// returns true if log level is INFO or lower + static bool inline isInfoEnabled() { return isLogLevelEnabled( LL_INFO ); } + /// returns true if log level is WARN or lower + static bool inline isWarnEnabled() { return isLogLevelEnabled( LL_WARN ); } + static void fatal( const char * msg, ... ); + static void error( const char * msg, ... ); + static void warn( const char * msg, ... ); + static void info( const char * msg, ... ); + static void debug( const char * msg, ... ); + static void trace( const char * msg, ... ); + /// sets logger instance + static void setLogger( CRLog * logger ); + virtual ~CRLog(); + + /// write log to specified file, flush after every message if autoFlush parameter is true + static void setFileLogger( const char * fname, bool autoFlush=false ); + /// use stdout for output + static void setStdoutLogger(); + /// use stderr for output + static void setStderrLogger(); +protected: + CRLog(); + virtual void log( const char * level, const char * msg, va_list args ) = 0; + log_level curr_level; + static CRLog * CRLOG; +}; + +#endif // __CR_LOG_H_INCLUDED__ diff --git a/crengine/include/crskin.h b/crengine/include/crskin.h index fbd7c6cdc7..6fd4ffa3ea 100644 --- a/crengine/include/crskin.h +++ b/crengine/include/crskin.h @@ -24,6 +24,7 @@ #include "lvptrvec.h" #include "lvref.h" #include "lvstring.h" +#include "crlog.h" #include "lvfntman.h" #include "lvdrawbuf.h" #include "lvtinydom.h" diff --git a/crengine/include/crtest.h b/crengine/include/crtest.h index b3a7097975..f363c232cc 100644 --- a/crengine/include/crtest.h +++ b/crengine/include/crtest.h @@ -4,6 +4,7 @@ #include "lvtypes.h" #include "lvstring.h" #include "lvstream.h" +#include "crlog.h" #define MYASSERT(x,t) \ if (!(x)) { \ diff --git a/crengine/include/crtimerutil.h b/crengine/include/crtimerutil.h new file mode 100644 index 0000000000..87d25f08e3 --- /dev/null +++ b/crengine/include/crtimerutil.h @@ -0,0 +1,93 @@ +/** \file crtimerutil.h + \brief timer to interval expiration + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + See LICENSE file for details. +*/ + +#ifndef __CR_TIMERUTIL_H_INCLUDED__ +#define __CR_TIMERUTIL_H_INCLUDED__ + +#include "lvtypes.h" + +#ifdef _WIN32 +#include +#else +#include +#endif + +/// timer to interval expiration, in milliseconds +class CRTimerUtil { + lInt64 _start; + volatile lInt64 _interval; +public: + static lInt64 getSystemTimeMillis() { +#ifdef _WIN32 + FILETIME ts; + GetSystemTimeAsFileTime(&ts); + return ((lInt64)ts.dwLowDateTime)/10000 + ((lInt64)ts.dwHighDateTime)*1000; +#else + timeval ts; + gettimeofday(&ts, 0); + return ((lInt64)ts.tv_usec)/1000 + ((lInt64)ts.tv_sec)*1000; +#endif + } + + /// create timer with infinite limit + CRTimerUtil() { + _start = getSystemTimeMillis(); + _interval = -1; + } + + /// create timer with limited interval (milliseconds) + CRTimerUtil(lInt64 expirationIntervalMillis) { + _start = getSystemTimeMillis(); + _interval = expirationIntervalMillis; + } + + CRTimerUtil(const CRTimerUtil & t) { + _start = t._start; + _interval = t._interval; + } + + void restart() { + _start = getSystemTimeMillis(); + } + + void restart(lInt64 expirationIntervalMillis) { + _start = getSystemTimeMillis(); + _interval = expirationIntervalMillis; + } + + CRTimerUtil & operator = (const CRTimerUtil & t) { + _start = t._start; + _interval = t._interval; + return *this; + } + + void cancel() { + _interval = 0; + } + + /// returns true if timeout is infinite + bool infinite() { + return _interval==-1; + } + /// returns true if expirationIntervalMillis is expired + bool expired() { + if ( _interval==-1 ) + return false; + return getSystemTimeMillis() - _start >= _interval; + } + /// return milliseconds elapsed since timer start + lInt64 elapsed() { + return getSystemTimeMillis() - _start; + } + int interval() { + return (int)_interval; + } +}; + +#endif // __CR_TIMERUTIL_H_INCLUDED__ diff --git a/crengine/include/lstridmap.h b/crengine/include/lstridmap.h index 15db4afc4a..ecd95d9fcb 100644 --- a/crengine/include/lstridmap.h +++ b/crengine/include/lstridmap.h @@ -21,6 +21,7 @@ #include struct css_elem_def_props_t; +class SerialBuf; //=========================================== class LDOMNameIdMapItem diff --git a/crengine/include/lvembeddedfont.h b/crengine/include/lvembeddedfont.h index 917142dbe4..07c29925ea 100644 --- a/crengine/include/lvembeddedfont.h +++ b/crengine/include/lvembeddedfont.h @@ -19,6 +19,7 @@ #include "lvstring.h" #include "lvptrvec.h" +class SerialBuf; class LVEmbeddedFontDef { lString16 _url; diff --git a/crengine/include/lvfntman.h b/crengine/include/lvfntman.h index f51b36decd..3de5b65d4d 100644 --- a/crengine/include/lvfntman.h +++ b/crengine/include/lvfntman.h @@ -18,6 +18,7 @@ #include #include "crsetup.h" #include "lvstring.h" +#include "lvstring16collection.h" #include "lvfont.h" diff --git a/crengine/include/lvpagesplitter.h b/crengine/include/lvpagesplitter.h index d25d2b5faa..3e47130960 100644 --- a/crengine/include/lvpagesplitter.h +++ b/crengine/include/lvpagesplitter.h @@ -21,6 +21,7 @@ #include "lvref.h" #include "lvstring.h" #include "lvhashtable.h" +#include "crtimerutil.h" #ifndef RENDER_PROGRESS_INTERVAL_MILLIS #define RENDER_PROGRESS_INTERVAL_MILLIS 1200 @@ -52,6 +53,8 @@ enum page_type_t { PAGE_TYPE_COVER = 1 }; +class SerialBuf; + /// footnote fragment inside page class LVPageFootNoteInfo { public: diff --git a/crengine/include/lvstream.h b/crengine/include/lvstream.h index 1c486887a5..7a881326a9 100644 --- a/crengine/include/lvstream.h +++ b/crengine/include/lvstream.h @@ -32,6 +32,7 @@ #include "lvstring.h" #include "lvarray.h" #include "lvptrvec.h" +#include "crtimerutil.h" #if LVLONG_FILE_SUPPORT == 1 typedef lUInt64 lvsize_t; ///< file size type diff --git a/crengine/include/lvstring.h b/crengine/include/lvstring.h index e34ff95ce2..a9d040f5c2 100644 --- a/crengine/include/lvstring.h +++ b/crengine/include/lvstring.h @@ -744,151 +744,9 @@ const lString16 & cs16(const char * str); /// get reference to atomic constant wide string for string literal e.g. cs16(L"abc") -- fast and memory effective replacement of lString16(L"abc") const lString16 & cs16(const lChar16 * str); -/// collection of wide strings -class lString16Collection -{ -private: - lstring16_chunk_t * * chunks; - int count; - int size; -public: - lString16Collection() - : chunks(NULL), count(0), size(0) - { } - /// parse delimiter-separated string - void parse( lString16 string, lChar16 delimiter, bool flgTrim ); - /// parse delimiter-separated string - void parse( lString16 string, lString16 delimiter, bool flgTrim ); - void reserve(int space); - int add( const lString16 & str ); - int add(const lChar16 * str) { return add(lString16(str)); } - int add(const lChar8 * str) { return add(lString16(str)); } - void addAll( const lString16Collection & v ) - { - for (int i=0; i class lStringBuf16 { - lString16 & str; - lChar16 buf[BUFSIZE]; - int pos; - lStringBuf16 & operator = (const lStringBuf16 & v) - { - CR_UNUSED(v); - // not available - return *this; - } -public: - lStringBuf16( lString16 & s ) - : str(s), pos(0) - { - } - inline void append( lChar16 ch ) - { - buf[ pos++ ] = ch; - if ( pos==BUFSIZE ) - flush(); - } - inline lStringBuf16& operator << ( lChar16 ch ) - { - buf[ pos++ ] = ch; - if ( pos==BUFSIZE ) - flush(); - return *this; - } - inline void flush() - { - str.append( buf, pos ); - pos = 0; - } - ~lStringBuf16( ) - { - flush(); - } -}; - -/// fast 8-bit string character appender -template class lStringBuf8 { - lString8 & str; - lChar8 buf[BUFSIZE]; - int pos; -public: - lStringBuf8( lString8 & s ) - : str(s), pos(0) - { - } - inline void append( lChar8 ch ) - { - buf[ pos++ ] = ch; - if ( pos==BUFSIZE ) - flush(); - } - inline lStringBuf8& operator << ( lChar8 ch ) - { - buf[ pos++ ] = ch; - if ( pos==BUFSIZE ) - flush(); - return *this; - } - inline void flush() - { - str.append( buf, pos ); - pos = 0; - } - ~lStringBuf8( ) - { - flush(); - } -}; - lString8 UnicodeToTranslit( const lString16 & str ); /// converts wide unicode string to local 8-bit encoding lString8 UnicodeToLocal( const lString16 & str ); @@ -1065,191 +849,8 @@ int TrimDoubleSpaces(lChar16 * buf, int len, bool allowStartSpace, bool allowEn #define LCSTR(x) (UnicodeToUtf8(x).c_str()) bool splitIntegerList( lString16 s, lString16 delim, int & value1, int & value2 ); -/// serialization/deserialization buffer -class SerialBuf -{ - lUInt8 * _buf; - bool _ownbuf; - bool _error; - bool _autoresize; - int _size; - int _pos; -public: - /// swap content of buffer with another buffer - void swap( SerialBuf & v ); - /// constructor of serialization buffer - SerialBuf( int sz, bool autoresize = true ); - SerialBuf( const lUInt8 * p, int sz ); - ~SerialBuf(); - - void set( lUInt8 * buf, int size ) - { - if ( _buf && _ownbuf ) - free( _buf ); - _buf = buf; - _ownbuf = true; - _error = false; - _autoresize = true; - _size = _pos = size; - } - bool copyTo( lUInt8 * buf, int maxSize ); - inline lUInt8 * buf() { return _buf; } - inline void setPos( int pos ) { _pos = pos; } - inline int space() const { return _size-_pos; } - inline int pos() const { return _pos; } - inline int size() const { return _size; } - - /// returns true if error occured during one of operations - inline bool error() const { return _error; } - - inline void seterror() { _error = true; } - /// move pointer to beginning, clear error flag - inline void reset() { _error = false; _pos = 0; } - - /// checks whether specified number of bytes is available, returns true in case of error - bool check( int reserved ); - - // write methods - /// put magic signature - void putMagic( const char * s ); - - /// add CRC32 for last N bytes - void putCRC( int N ); - - /// returns CRC32 for the whole buffer - lUInt32 getCRC(); - - /// add contents of another buffer - SerialBuf & operator << ( const SerialBuf & v ); - - SerialBuf & operator << ( lUInt8 n ); - - SerialBuf & operator << ( char n ); - - SerialBuf & operator << ( bool n ); - - SerialBuf & operator << ( lUInt16 n ); - - SerialBuf & operator << ( lInt16 n ); - - SerialBuf & operator << ( lUInt32 n ); - - SerialBuf & operator << ( lInt32 n ); - - SerialBuf & operator << ( const lString16 & s ); - - SerialBuf & operator << ( const lString8 & s8 ); - - // read methods - SerialBuf & operator >> ( lUInt8 & n ); - - SerialBuf & operator >> ( char & n ); - - SerialBuf & operator >> ( bool & n ); - - SerialBuf & operator >> ( lUInt16 & n ); - - SerialBuf & operator >> ( lInt16 & n ); - - SerialBuf & operator >> ( lUInt32 & n ); - - SerialBuf & operator >> ( lInt32 & n ); - - SerialBuf & operator >> ( lString8 & s8 ); - - SerialBuf & operator >> ( lString16 & s ); - - bool checkMagic( const char * s ); - /// read crc32 code, comapare with CRC32 for last N bytes - bool checkCRC( int N ); -}; - - -/// Logger -class CRLog -{ -public: - /// log levels - enum log_level { - LL_FATAL, - LL_ERROR, - LL_WARN, - LL_INFO, - LL_DEBUG, - LL_TRACE - }; - /// set current log level - static void setLogLevel( log_level level ); - /// returns current log level - static log_level getLogLevel(); - /// returns true if specified log level is enabled - static bool isLogLevelEnabled( log_level level ); - /// returns true if log level is DEBUG or lower - static bool inline isDebugEnabled() { return isLogLevelEnabled( LL_DEBUG ); } - /// returns true if log level is TRACE - static bool inline isTraceEnabled() { return isLogLevelEnabled( LL_TRACE ); } - /// returns true if log level is INFO or lower - static bool inline isInfoEnabled() { return isLogLevelEnabled( LL_INFO ); } - /// returns true if log level is WARN or lower - static bool inline isWarnEnabled() { return isLogLevelEnabled( LL_WARN ); } - static void fatal( const char * msg, ... ); - static void error( const char * msg, ... ); - static void warn( const char * msg, ... ); - static void info( const char * msg, ... ); - static void debug( const char * msg, ... ); - static void trace( const char * msg, ... ); - /// sets logger instance - static void setLogger( CRLog * logger ); - virtual ~CRLog(); - - /// write log to specified file, flush after every message if autoFlush parameter is true - static void setFileLogger( const char * fname, bool autoFlush=false ); - /// use stdout for output - static void setStdoutLogger(); - /// use stderr for output - static void setStderrLogger(); -protected: - CRLog(); - virtual void log( const char * level, const char * msg, va_list args ) = 0; - log_level curr_level; - static CRLog * CRLOG; -}; - - +#if LDOM_USE_OWN_MEM_MAN==1 void free_ls_storage(); - -lUInt64 GetCurrentTimeMillis(); -void CRReinitTimer(); - - - -#ifdef _DEBUG -#include -class DumpFile -{ -public: - FILE * f; - DumpFile( const char * fname ) - : f(NULL) - { - if ( fname ) - f = fopen( fname, "at" ); - if ( !f ) - f = stdout; - fprintf(f, "DumpFile log started\n"); - } - ~DumpFile() - { - if ( f!=stdout ) - fclose(f); - } - operator FILE * () { if (f) fflush(f); return f?f:stdout; } -}; -#endif - #endif - - - - +#endif // __LV_STRING_H_INCLUDED__ diff --git a/crengine/include/lvstring16collection.h b/crengine/include/lvstring16collection.h new file mode 100644 index 0000000000..2d85de4378 --- /dev/null +++ b/crengine/include/lvstring16collection.h @@ -0,0 +1,74 @@ +/** \file lvstring16collection.h + \brief collection of wide strings + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. +*/ + +#ifndef __LV_STRING16COLLECTION_H_INCLUDED__ +#define __LV_STRING16COLLECTION_H_INCLUDED__ + +#include "lvstring.h" + +/// collection of wide strings +class lString16Collection +{ +private: + lstring16_chunk_t * * chunks; + int count; + int size; +public: + lString16Collection() + : chunks(NULL), count(0), size(0) + { } + /// parse delimiter-separated string + void parse( lString16 string, lChar16 delimiter, bool flgTrim ); + /// parse delimiter-separated string + void parse( lString16 string, lString16 delimiter, bool flgTrim ); + void reserve(int space); + int add( const lString16 & str ); + int add(const lChar16 * str) { return add(lString16(str)); } + int add(const lChar8 * str) { return add(lString16(str)); } + void addAll( const lString16Collection & v ) + { + for (int i=0; i #include "crsetup.h" -#ifdef _WIN32 -#include -#else -#include -#endif - #ifdef _WIN32 typedef long lInt32; ///< signed 32 bit int typedef unsigned long lUInt32; ///< unsigned 32 bit int @@ -315,78 +309,6 @@ class lvByteOrderConv { } }; -/// timer to interval expiration, in milliseconds -class CRTimerUtil { - lInt64 _start; - volatile lInt64 _interval; -public: - static lInt64 getSystemTimeMillis() { -#ifdef _WIN32 - FILETIME ts; - GetSystemTimeAsFileTime(&ts); - return ((lInt64)ts.dwLowDateTime)/10000 + ((lInt64)ts.dwHighDateTime)*1000; -#else - timeval ts; - gettimeofday(&ts, 0); - return ((lInt64)ts.tv_usec)/1000 + ((lInt64)ts.tv_sec)*1000; -#endif - } - - /// create timer with infinite limit - CRTimerUtil() { - _start = getSystemTimeMillis(); - _interval = -1; - } - - /// create timer with limited interval (milliseconds) - CRTimerUtil(lInt64 expirationIntervalMillis) { - _start = getSystemTimeMillis(); - _interval = expirationIntervalMillis; - } - - CRTimerUtil(const CRTimerUtil & t) { - _start = t._start; - _interval = t._interval; - } - - void restart() { - _start = getSystemTimeMillis(); - } - - void restart(lInt64 expirationIntervalMillis) { - _start = getSystemTimeMillis(); - _interval = expirationIntervalMillis; - } - - CRTimerUtil & operator = (const CRTimerUtil & t) { - _start = t._start; - _interval = t._interval; - return *this; - } - - void cancel() { - _interval = 0; - } - - /// returns true if timeout is infinite - bool infinite() { - return _interval==-1; - } - /// returns true if expirationIntervalMillis is expired - bool expired() { - if ( _interval==-1 ) - return false; - return getSystemTimeMillis() - _start >= _interval; - } - /// return milliseconds elapsed since timer start - lInt64 elapsed() { - return getSystemTimeMillis() - _start; - } - int interval() { - return (int)_interval; - } -}; - // MACROS to avoid UNUSED PARAM warning #define CR_UNUSED(x) (void)x; #define CR_UNUSED2(x,x2) (void)x;(void)x2; diff --git a/crengine/include/props.h b/crengine/include/props.h index d0a1339cdd..3d0d2a9a27 100644 --- a/crengine/include/props.h +++ b/crengine/include/props.h @@ -19,6 +19,7 @@ class CRPropAccessor; typedef LVFastRef CRPropRef; +class SerialBuf; /// interface to get/set properties class CRPropAccessor : public LVRefCounter { diff --git a/crengine/include/serialbuf.h b/crengine/include/serialbuf.h new file mode 100644 index 0000000000..d723ef46c7 --- /dev/null +++ b/crengine/include/serialbuf.h @@ -0,0 +1,118 @@ +/** \file serialbuf.h + \brief serialization buffer interface + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. +*/ + +#ifndef __LV_SERIALBUF_H_INCLUDED__ +#define __LV_SERIALBUF_H_INCLUDED__ + +#include "lvtypes.h" +#include "lvstring.h" + +/// serialization/deserialization buffer +class SerialBuf +{ + lUInt8 * _buf; + bool _ownbuf; + bool _error; + bool _autoresize; + int _size; + int _pos; +public: + /// swap content of buffer with another buffer + void swap( SerialBuf & v ); + /// constructor of serialization buffer + SerialBuf( int sz, bool autoresize = true ); + SerialBuf( const lUInt8 * p, int sz ); + ~SerialBuf(); + + void set( lUInt8 * buf, int size ) + { + if ( _buf && _ownbuf ) + free( _buf ); + _buf = buf; + _ownbuf = true; + _error = false; + _autoresize = true; + _size = _pos = size; + } + bool copyTo( lUInt8 * buf, int maxSize ); + inline lUInt8 * buf() { return _buf; } + inline void setPos( int pos ) { _pos = pos; } + inline int space() const { return _size-_pos; } + inline int pos() const { return _pos; } + inline int size() const { return _size; } + + /// returns true if error occured during one of operations + inline bool error() const { return _error; } + + inline void seterror() { _error = true; } + /// move pointer to beginning, clear error flag + inline void reset() { _error = false; _pos = 0; } + + /// checks whether specified number of bytes is available, returns true in case of error + bool check( int reserved ); + + // write methods + /// put magic signature + void putMagic( const char * s ); + + /// add CRC32 for last N bytes + void putCRC( int N ); + + /// returns CRC32 for the whole buffer + lUInt32 getCRC(); + + /// add contents of another buffer + SerialBuf & operator << ( const SerialBuf & v ); + + SerialBuf & operator << ( lUInt8 n ); + + SerialBuf & operator << ( char n ); + + SerialBuf & operator << ( bool n ); + + SerialBuf & operator << ( lUInt16 n ); + + SerialBuf & operator << ( lInt16 n ); + + SerialBuf & operator << ( lUInt32 n ); + + SerialBuf & operator << ( lInt32 n ); + + SerialBuf & operator << ( const lString16 & s ); + + SerialBuf & operator << ( const lString8 & s8 ); + + // read methods + SerialBuf & operator >> ( lUInt8 & n ); + + SerialBuf & operator >> ( char & n ); + + SerialBuf & operator >> ( bool & n ); + + SerialBuf & operator >> ( lUInt16 & n ); + + SerialBuf & operator >> ( lInt16 & n ); + + SerialBuf & operator >> ( lUInt32 & n ); + + SerialBuf & operator >> ( lInt32 & n ); + + SerialBuf & operator >> ( lString8 & s8 ); + + SerialBuf & operator >> ( lString16 & s ); + + bool checkMagic( const char * s ); + /// read crc32 code, comapare with CRC32 for last N bytes + bool checkCRC( int N ); +}; + +#endif // __LV_SERIALBUF_H_INCLUDED__ diff --git a/crengine/obsolete/lvstringbuf16.h b/crengine/obsolete/lvstringbuf16.h new file mode 100644 index 0000000000..78c7855915 --- /dev/null +++ b/crengine/obsolete/lvstringbuf16.h @@ -0,0 +1,58 @@ +/** \file lvstringbuf16.h + \brief string classes interface + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. +*/ + +#ifndef __LV_STRINGBUF16_H_INCLUDED__ +#define __LV_STRINGBUF16_H_INCLUDED__ + +#include "../include/lvstring.h" + +/// fast 16-bit string character appender +template class lStringBuf16 { + lString16 & str; + lChar16 buf[BUFSIZE]; + int pos; + lStringBuf16 & operator = (const lStringBuf16 & v) + { + CR_UNUSED(v); + // not available + return *this; + } +public: + lStringBuf16( lString16 & s ) + : str(s), pos(0) + { + } + inline void append( lChar16 ch ) + { + buf[ pos++ ] = ch; + if ( pos==BUFSIZE ) + flush(); + } + inline lStringBuf16& operator << ( lChar16 ch ) + { + buf[ pos++ ] = ch; + if ( pos==BUFSIZE ) + flush(); + return *this; + } + inline void flush() + { + str.append( buf, pos ); + pos = 0; + } + ~lStringBuf16( ) + { + flush(); + } +}; + +#endif // __LV_STRINGBUF16_H_INCLUDED__ diff --git a/crengine/obsolete/lvstringbuf8.h b/crengine/obsolete/lvstringbuf8.h new file mode 100644 index 0000000000..2621fc4a30 --- /dev/null +++ b/crengine/obsolete/lvstringbuf8.h @@ -0,0 +1,52 @@ +/** \file lvstringbuf8.h + \brief string classes interface + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. +*/ + +#ifndef __LV_STRINGBUF8_H_INCLUDED__ +#define __LV_STRINGBUF8_H_INCLUDED__ + +#include "../include/lvstring.h" + +/// fast 8-bit string character appender +template class lStringBuf8 { + lString8 & str; + lChar8 buf[BUFSIZE]; + int pos; +public: + lStringBuf8( lString8 & s ) + : str(s), pos(0) + { + } + inline void append( lChar8 ch ) + { + buf[ pos++ ] = ch; + if ( pos==BUFSIZE ) + flush(); + } + inline lStringBuf8& operator << ( lChar8 ch ) + { + buf[ pos++ ] = ch; + if ( pos==BUFSIZE ) + flush(); + return *this; + } + inline void flush() + { + str.append( buf, pos ); + pos = 0; + } + ~lStringBuf8( ) + { + flush(); + } +}; + +#endif // __LV_STRINGBUF8_H_INCLUDED__ diff --git a/crengine/obsolete/readme.txt b/crengine/obsolete/readme.txt new file mode 100644 index 0000000000..4a886ca726 --- /dev/null +++ b/crengine/obsolete/readme.txt @@ -0,0 +1 @@ +This classes is unused, saved for history! diff --git a/crengine/src/chmfmt.cpp b/crengine/src/chmfmt.cpp index 9ef341165a..fde8db0235 100644 --- a/crengine/src/chmfmt.cpp +++ b/crengine/src/chmfmt.cpp @@ -3,6 +3,7 @@ //#define CHM_SUPPORT_ENABLED 1 #if CHM_SUPPORT_ENABLED==1 #include "../include/chmfmt.h" +#include "../include/crlog.h" #include #define DUMP_CHM_DOC 0 diff --git a/crengine/src/crconcurrent.cpp b/crengine/src/crconcurrent.cpp index bfbd48bf8b..7e61574d15 100644 --- a/crengine/src/crconcurrent.cpp +++ b/crengine/src/crconcurrent.cpp @@ -2,6 +2,7 @@ #include "crconcurrent.h" #include "lvptrvec.h" #include "lvstring.h" +#include "crlog.h" CRMutex * _refMutex = NULL; CRMutex * _fontMutex = NULL; diff --git a/crengine/src/cri18n.cpp b/crengine/src/cri18n.cpp index 45501c9ca4..98f72027d8 100644 --- a/crengine/src/cri18n.cpp +++ b/crengine/src/cri18n.cpp @@ -14,6 +14,8 @@ #include "../include/cri18n.h" #include "../include/lvstream.h" +#include "../include/crlog.h" +#include "../include/lvstring8collection.h" CRI18NTranslator * CRI18NTranslator::_translator = NULL; CRI18NTranslator * CRI18NTranslator::_defTranslator = NULL; diff --git a/crengine/src/crlog.cpp b/crengine/src/crlog.cpp new file mode 100644 index 0000000000..7e99918af6 --- /dev/null +++ b/crengine/src/crlog.cpp @@ -0,0 +1,257 @@ +/******************************************************* + + CoolReader Engine + + crlog.cpp: logging classes implementation + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License + See LICENSE file for details + +*******************************************************/ + +#include "../include/crlog.h" +#include "lvtypes.h" + +#include +#include +#include + +class CRFileLogger; + +CRLog * CRLog::CRLOG = NULL; +void CRLog::setLogger( CRLog * logger ) +{ + if ( CRLOG!=NULL ) { + delete CRLOG; + } + CRLOG = logger; +} + +void CRLog::setLogLevel( CRLog::log_level level ) +{ + if ( !CRLOG ) + return; + warn( "Changing log level from %d to %d", (int)CRLOG->curr_level, (int)level ); + CRLOG->curr_level = level; +} + +CRLog::log_level CRLog::getLogLevel() +{ + if ( !CRLOG ) + return LL_INFO; + return CRLOG->curr_level; +} + +bool CRLog::isLogLevelEnabled( CRLog::log_level level ) +{ + if ( !CRLOG ) + return false; + return (CRLOG->curr_level >= level); +} + +void CRLog::fatal( const char * msg, ... ) +{ + if ( !CRLOG ) + return; + va_list args; + va_start( args, msg ); + CRLOG->log( "FATAL", msg, args ); + va_end(args); +} + +void CRLog::error( const char * msg, ... ) +{ + if ( !CRLOG || CRLOG->curr_levellog( "ERROR", msg, args ); + va_end(args); +} + +void CRLog::warn( const char * msg, ... ) +{ + if ( !CRLOG || CRLOG->curr_levellog( "WARN", msg, args ); + va_end(args); +} + +void CRLog::info( const char * msg, ... ) +{ + if ( !CRLOG || CRLOG->curr_levellog( "INFO", msg, args ); + va_end(args); +} + +void CRLog::debug( const char * msg, ... ) +{ + if ( !CRLOG || CRLOG->curr_levellog( "DEBUG", msg, args ); + va_end(args); +} + +void CRLog::trace( const char * msg, ... ) +{ + if ( !CRLOG || CRLOG->curr_levellog( "TRACE", msg, args ); + va_end(args); +} + +CRLog::CRLog() + : curr_level(LL_INFO) +{ +} + +CRLog::~CRLog() +{ +} + + +// private class CRFileLogger + +#ifndef LOG_HEAP_USAGE +#define LOG_HEAP_USAGE 0 +#endif + +#ifdef _WIN32 +static bool __timerInitialized = false; +static double __timeTicksPerMillis; +static lUInt64 __timeStart; +static lUInt64 __timeAbsolute; +static lUInt64 __startTimeMillis; + +static void CRReinitTimer() { + LARGE_INTEGER tps; + QueryPerformanceFrequency(&tps); + __timeTicksPerMillis = (double)(tps.QuadPart / 1000L); + LARGE_INTEGER queryTime; + QueryPerformanceCounter(&queryTime); + __timeStart = (lUInt64)(queryTime.QuadPart / __timeTicksPerMillis); + __timerInitialized = true; + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + __startTimeMillis = (ft.dwLowDateTime | (((lUInt64)ft.dwHighDateTime) << 32)) / 10000; +} +#endif + +static lUInt64 GetCurrentTimeMillis() { +#if defined(LINUX) || defined(ANDROID) || defined(_LINUX) + timeval ts; + gettimeofday(&ts, NULL); + return ts.tv_sec * (lUInt64)1000 + ts.tv_usec / 1000; +#else +#ifdef _WIN32 + if (!__timerInitialized) { + CRReinitTimer(); + return __startTimeMillis; + } else { + LARGE_INTEGER queryTime; + QueryPerformanceCounter(&queryTime); + __timeAbsolute = (lUInt64)(queryTime.QuadPart / __timeTicksPerMillis); + return __startTimeMillis + (lUInt64)(__timeAbsolute - __timeStart); + } +#else +#error * You should define GetCurrentTimeMillis() * +#endif +#endif +} + +class CRFileLogger : public CRLog +{ +protected: + FILE * f; + bool autoClose; + bool autoFlush; + virtual void log( const char * level, const char * msg, va_list args ) + { + if ( !f ) + return; +#ifdef LINUX + struct timeval tval; + gettimeofday( &tval, NULL ); + int ms = tval.tv_usec; + time_t t = tval.tv_sec; +#if LOG_HEAP_USAGE + struct mallinfo mi = mallinfo(); + int memusage = mi.arena; +#endif +#else + lUInt64 ts = GetCurrentTimeMillis(); + //time_t t = (time_t)time(0); + time_t t = ts / 1000; + int ms = (ts % 1000) * 1000; +#if LOG_HEAP_USAGE + int memusage = 0; +#endif +#endif + struct tm * bt = localtime(&t); +#if LOG_HEAP_USAGE + fprintf(f, "%04d/%02d/%02d %02d:%02d:%02d.%04d [%d] %s ", bt->tm_year+1900, bt->tm_mon+1, bt->tm_mday, bt->tm_hour, bt->tm_min, bt->tm_sec, ms/100, memusage, level); +#else + fprintf(f, "%04d/%02d/%02d %02d:%02d:%02d.%04d %s ", bt->tm_year+1900, bt->tm_mon+1, bt->tm_mday, bt->tm_hour, bt->tm_min, bt->tm_sec, ms/100, level); +#endif + vfprintf( f, msg, args ); + fprintf(f, "\n" ); + if ( autoFlush ) + fflush( f ); + } +public: + CRFileLogger( FILE * file, bool _autoClose, bool _autoFlush ) + : f(file), autoClose(_autoClose), autoFlush( _autoFlush ) + { + info( "Started logging" ); + } + CRFileLogger( const char * fname, bool _autoFlush ) + : f(fopen( fname, "wt" )), autoClose(true), autoFlush( _autoFlush ) + { + static unsigned char utf8sign[] = {0xEF, 0xBB, 0xBF}; + static const char * log_level_names[] = { + "FATAL", + "ERROR", + "WARN", + "INFO", + "DEBUG", + "TRACE", + }; + fwrite( utf8sign, 3, 1, f); + info( "Started logging. Level=%s", log_level_names[getLogLevel()] ); + } + virtual ~CRFileLogger() { + if ( f && autoClose ) { + info( "Stopped logging" ); + fclose( f ); + } + f = NULL; + } +}; + + +// CRLog +void CRLog::setFileLogger( const char * fname, bool autoFlush ) +{ + setLogger( new CRFileLogger( fname, autoFlush ) ); +} + +void CRLog::setStdoutLogger() +{ + setLogger( new CRFileLogger( (FILE*)stdout, false, true ) ); +} + +void CRLog::setStderrLogger() +{ + setLogger( new CRFileLogger( (FILE*)stderr, false, true ) ); +} diff --git a/crengine/src/crtxtenc.cpp b/crengine/src/crtxtenc.cpp index 8cd632562e..deeee01234 100644 --- a/crengine/src/crtxtenc.cpp +++ b/crengine/src/crtxtenc.cpp @@ -14,6 +14,7 @@ #include "../include/crtxtenc.h" #include "../include/lvstring.h" #include "../include/cp_stats.h" +#include "../include/crlog.h" #include #include diff --git a/crengine/src/docxfmt.cpp b/crengine/src/docxfmt.cpp index 8bb9fa8d9b..290f094740 100644 --- a/crengine/src/docxfmt.cpp +++ b/crengine/src/docxfmt.cpp @@ -2,6 +2,7 @@ #include "../include/lvtinydom.h" #include "../include/fb2def.h" #include "../include/lvopc.h" +#include "../include/crlog.h" #define DOCX_TAG_NAME(itm) docx_el_##itm##_name #define DOCX_TAG_ID(itm) docx_el_##itm diff --git a/crengine/src/epubfmt.cpp b/crengine/src/epubfmt.cpp index a0679f5aa6..7f77c81b0f 100644 --- a/crengine/src/epubfmt.cpp +++ b/crengine/src/epubfmt.cpp @@ -1,4 +1,5 @@ #include "../include/epubfmt.h" +#include "../include/crlog.h" class EpubItem { diff --git a/crengine/src/fb3fmt.cpp b/crengine/src/fb3fmt.cpp index a1e94e6a1e..a477c0e0b4 100644 --- a/crengine/src/fb3fmt.cpp +++ b/crengine/src/fb3fmt.cpp @@ -2,6 +2,7 @@ #include "../include/lvtinydom.h" #include "../include/fb2def.h" #include "../include/lvopc.h" +#include "../include/crlog.h" static const lChar16 * const fb3_BodyContentType = L"application/fb3-body+xml"; static const lChar16 * const fb3_DescriptionContentType = L"application/fb3-description+xml"; diff --git a/crengine/src/hist.cpp b/crengine/src/hist.cpp index 58b9c0c3eb..52762a7757 100644 --- a/crengine/src/hist.cpp +++ b/crengine/src/hist.cpp @@ -11,6 +11,7 @@ #include "../include/lvtinydom.h" #include "../include/hist.h" +#include "../include/crlog.h" void CRFileHist::clear() { diff --git a/crengine/src/hyphman.cpp b/crengine/src/hyphman.cpp index 38e2442cd6..5f0d3bce14 100644 --- a/crengine/src/hyphman.cpp +++ b/crengine/src/hyphman.cpp @@ -38,6 +38,8 @@ #include "../include/hyphman.h" #include "../include/lvfnt.h" #include "../include/lvstring.h" +#include "../include/lvstring16collection.h" +#include "../include/crlog.h" #ifdef ANDROID diff --git a/crengine/src/lvdrawbuf.cpp b/crengine/src/lvdrawbuf.cpp index 53e91959b6..1aaaa13a89 100644 --- a/crengine/src/lvdrawbuf.cpp +++ b/crengine/src/lvdrawbuf.cpp @@ -16,6 +16,7 @@ #include #include #include "../include/lvdrawbuf.h" +#include "../include/crlog.h" #define GUARD_BYTE 0xa5 #define CHECK_GUARD_BYTE \ diff --git a/crengine/src/lvembeddedfont.cpp b/crengine/src/lvembeddedfont.cpp index fddd953d0c..e4ceafe061 100644 --- a/crengine/src/lvembeddedfont.cpp +++ b/crengine/src/lvembeddedfont.cpp @@ -13,6 +13,7 @@ */ #include "../include/lvembeddedfont.h" +#include "../include/serialbuf.h" #include diff --git a/crengine/src/lvfntman.cpp b/crengine/src/lvfntman.cpp index 4e54acd5c8..e082981556 100644 --- a/crengine/src/lvfntman.cpp +++ b/crengine/src/lvfntman.cpp @@ -14,6 +14,7 @@ #include "../include/lvfntman.h" #include "../include/lvstyles.h" +#include "../include/crlog.h" #include "private/lvfreetypefontman.h" #include "private/lvwin32fontman.h" #include "private/lvbitmapfontman.h" diff --git a/crengine/src/lvimg.cpp b/crengine/src/lvimg.cpp index 810b13b1f9..ebfc3073ca 100644 --- a/crengine/src/lvimg.cpp +++ b/crengine/src/lvimg.cpp @@ -20,6 +20,7 @@ #include "../include/lvimg.h" #include "../include/lvtinydom.h" +#include "../include/crlog.h" #if (USE_LIBPNG==1) #include diff --git a/crengine/src/lvmemman.cpp b/crengine/src/lvmemman.cpp index a7fba470f0..2120fc0d33 100644 --- a/crengine/src/lvmemman.cpp +++ b/crengine/src/lvmemman.cpp @@ -15,6 +15,7 @@ #include "../include/lvmemman.h" #include "../include/lvref.h" #include "../include/lvtinydom.h" +#include "../include/crlog.h" #ifdef _LINUX #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE diff --git a/crengine/src/lvopc.cpp b/crengine/src/lvopc.cpp index ab5c4b7086..3fd02cfc91 100644 --- a/crengine/src/lvopc.cpp +++ b/crengine/src/lvopc.cpp @@ -1,5 +1,6 @@ #include "../include/lvopc.h" #include "../include/lvtinydom.h" +#include "../include/crlog.h" static const lChar16 * const OPC_PropertiesContentType = L"application/vnd.openxmlformats-package.core-properties+xml"; diff --git a/crengine/src/lvpagesplitter.cpp b/crengine/src/lvpagesplitter.cpp index 725ff01094..79676a80f1 100644 --- a/crengine/src/lvpagesplitter.cpp +++ b/crengine/src/lvpagesplitter.cpp @@ -12,6 +12,8 @@ #include "../include/lvpagesplitter.h" #include "../include/lvtinydom.h" +#include "../include/crlog.h" +#include "../include/serialbuf.h" #include diff --git a/crengine/src/lvrend.cpp b/crengine/src/lvrend.cpp index 21b3426b0e..025bc855b6 100644 --- a/crengine/src/lvrend.cpp +++ b/crengine/src/lvrend.cpp @@ -16,6 +16,7 @@ #include "../include/lvtinydom.h" #include "../include/fb2def.h" #include "../include/lvrend.h" +#include "../include/crlog.h" //#define DEBUG_TREE_DRAW 3 diff --git a/crengine/src/lvstream.cpp b/crengine/src/lvstream.cpp index 3f927364a5..bd0578c91d 100644 --- a/crengine/src/lvstream.cpp +++ b/crengine/src/lvstream.cpp @@ -27,6 +27,7 @@ #include "../include/lvstream.h" #include "../include/lvptrvec.h" #include "../include/crtxtenc.h" +#include "../include/crlog.h" #include #include #include diff --git a/crengine/src/lvstring.cpp b/crengine/src/lvstring.cpp index 54a92e12e1..29782b04d7 100644 --- a/crengine/src/lvstring.cpp +++ b/crengine/src/lvstring.cpp @@ -12,6 +12,7 @@ *******************************************************/ #include "../include/lvstring.h" + #include #include #include @@ -560,6 +561,12 @@ int lStr_cmp(const lChar16 * dst, const lChar8 * src) int lStr_cmp(const lChar8 * dst, const lChar16 * src) { + if (!dst && !src) + return 0; + if (!dst) + return -1; + else if (!src) + return 1; while ( (lChar16)*dst == *src) { if (! *dst ) @@ -1262,172 +1269,6 @@ lUInt32 lString16::getHash() const } - -void lString16Collection::reserve(int space) -{ - if ( count + space > size ) - { - int tmpSize = count + space + 64; - void* tmp = realloc( chunks, sizeof(lstring16_chunk_t *) * tmpSize ); - if (tmp) { - size = tmpSize; - chunks = (lstring16_chunk_t * *)tmp; - } - else { - // TODO: throw exception or change function prototype & return code - } - } -} - -static int (str16_comparator)(const void * n1, const void * n2) -{ - lstring16_chunk_t ** s1 = (lstring16_chunk_t **)n1; - lstring16_chunk_t ** s2 = (lstring16_chunk_t **)n2; - return lStr_cmp( (*s1)->data16(), (*s2)->data16() ); -} - -static int(*custom_lstr16_comparator_ptr)(lString16 & s1, lString16 & s2); -static int (str16_custom_comparator)(const void * n1, const void * n2) -{ - lString16 s1(*((lstring16_chunk_t **)n1)); - lString16 s2(*((lstring16_chunk_t **)n2)); - return custom_lstr16_comparator_ptr(s1, s2); -} - -void lString16Collection::sort(int(comparator)(lString16 & s1, lString16 & s2)) -{ - custom_lstr16_comparator_ptr = comparator; - qsort(chunks,count,sizeof(lstring16_chunk_t*), str16_custom_comparator); -} - -void lString16Collection::sort() -{ - qsort(chunks,count,sizeof(lstring16_chunk_t*), str16_comparator); -} - -int lString16Collection::add( const lString16 & str ) -{ - reserve( 1 ); - chunks[count] = str.pchunk; - str.addref(); - return count++; -} -void lString16Collection::clear() -{ - if (chunks) { - for (int i=0; i= count) - return; - int i; - for (i = offset; i < offset + cnt; i++) - { - ((lString16 *)chunks)[i].release(); - } - for (i = offset + cnt; i < count; i++) - { - chunks[i-cnt] = chunks[i]; - } - count -= cnt; - if (!count) - clear(); -} - -void lString8Collection::split( const lString8 & str, const lString8 & delimiter ) -{ - if (str.empty()) - return; - for (int startpos = 0; startpos < str.length(); ) { - int pos = str.pos(delimiter, startpos); - if (pos < 0) - pos = str.length(); - add(str.substr(startpos, pos - startpos)); - startpos = pos + delimiter.length(); - } -} - -void lString16Collection::split( const lString16 & str, const lString16 & delimiter ) -{ - if (str.empty()) - return; - for (int startpos = 0; startpos < str.length(); ) { - int pos = str.pos(delimiter, startpos); - if (pos < 0) - pos = str.length(); - add(str.substr(startpos, pos - startpos)); - startpos = pos + delimiter.length(); - } -} - -void lString8Collection::erase(int offset, int cnt) -{ - if (count <= 0) - return; - if (offset < 0 || offset + cnt > count) - return; - int i; - for (i = offset; i < offset + cnt; i++) - { - ((lString8 *)chunks)[i].release(); - } - for (i = offset + cnt; i < count; i++) - { - chunks[i-cnt] = chunks[i]; - } - count -= cnt; - if (!count) - clear(); -} - -void lString8Collection::reserve(int space) -{ - if ( count + space > size ) - { - int tmpSize = count + space + 64; - void* tmp = realloc( chunks, sizeof(lstring8_chunk_t *) * tmpSize ); - if (tmp) { - size = tmpSize; - chunks = (lstring8_chunk_t * *)tmp; - } - else { - // TODO: throw exception or change function prototype & return code - } - } -} - -int lString8Collection::add( const lString8 & str ) -{ - reserve( 1 ); - chunks[count] = str.pchunk; - str.addref(); - return count++; -} -void lString8Collection::clear() -{ - for (int i=0; i_pos ) { - *this << (lUInt32)0; - seterror(); - } - lUInt32 n = 0; - n = lStr_crc32( n, _buf + _pos-size, size ); - *this << n; -} - -/// get CRC32 for the whole buffer -lUInt32 SerialBuf::getCRC() -{ - if (error()) - return 0; - lUInt32 n = 0; - n = lStr_crc32( n, _buf, _pos ); - return n; -} - -/// read crc32 code, comapare with CRC32 for last N bytes -bool SerialBuf::checkCRC( int size ) -{ - if ( error() ) - return false; - if ( size>_pos ) { - seterror(); - return false; - } - lUInt32 n0 = 0; - n0 = lStr_crc32(n0, _buf + _pos-size, size); - lUInt32 n = 0; - *this >> n; - if ( error() ) - return false; - if ( n!=n0 ) - seterror(); - return !error(); -} - -/// deserialize from byte array (pointer will be incremented by number of bytes read) -bool lString16HashedCollection::deserialize( SerialBuf & buf ) -{ - if ( buf.error() ) - return false; - clear(); - int start = buf.pos(); - buf.putMagic( str_hash_magic ); - lInt32 count = 0; - buf >> count; - for ( int i=0; i> s; - if ( buf.error() ) - break; - add( s.c_str() ); - } - buf.checkCRC( buf.pos() - start ); - return !buf.error(); -} - -lString16HashedCollection::lString16HashedCollection( lString16HashedCollection & v ) -: lString16Collection( v ) -, hashSize( v.hashSize ) -, hash( NULL ) -{ - hash = (HashPair *)malloc( sizeof(HashPair) * hashSize ); - for ( int i=0; iindex ); - next = next->next; - } - } -} - -void lString16HashedCollection::addHashItem( int hashIndex, int storageIndex ) -{ - if ( hash[ hashIndex ].index == -1 ) { - hash[hashIndex].index = storageIndex; - } else { - HashPair * np = (HashPair *)malloc(sizeof(HashPair)); - np->index = storageIndex; - np->next = hash[hashIndex].next; - hash[hashIndex].next = np; - } -} - -void lString16HashedCollection::clearHash() -{ - if ( hash ) { - for ( int i=0; inext; - free( p ); - p = tmp; - } - } - free( hash ); - } - hash = NULL; -} - -lString16HashedCollection::lString16HashedCollection( lUInt32 hash_size ) -: hashSize(hash_size), hash(NULL) -{ - - hash = (HashPair *)malloc( sizeof(HashPair) * hashSize ); - for ( int i=0; inext ) { - const lString16 & str = at( p->index ); - if ( str==s ) - return p->index; - } - } - return -1; -} - -void lString16HashedCollection::reHash( int newSize ) -{ - if (hashSize == newSize) - return; - clearHash(); - hashSize = newSize; - if (hashSize > 0) { - hash = (HashPair *)malloc( sizeof(HashPair) * hashSize ); - for ( int i=0; inext ) { - const lString16 & str = at( p->index ); - if ( str==s ) - return p->index; - } - } - lUInt32 i = lString16Collection::add( lString16(s) ); - addHashItem( n, i ); - return i; -} const lString16 lString16::empty_str; @@ -2660,50 +2293,6 @@ void lStr_lowercase( lChar16 * str, int len ) } } -void lString16Collection::parse( lString16 string, lChar16 delimiter, bool flgTrim ) -{ - int wstart=0; - for ( int i=0; i<=string.length(); i++ ) { - if ( i==string.length() || string[i]==delimiter ) { - lString16 s( string.substr( wstart, i-wstart) ); - if ( flgTrim ) - s.trimDoubleSpaces(false, false, false); - if ( !flgTrim || !s.empty() ) - add( s ); - wstart = i+1; - } - } -} - -void lString16Collection::parse( lString16 string, lString16 delimiter, bool flgTrim ) -{ - if ( delimiter.empty() || string.pos(delimiter)<0 ) { - lString16 s( string ); - if ( flgTrim ) - s.trimDoubleSpaces(false, false, false); - add(s); - return; - } - int wstart=0; - for ( int i=0; i<=string.length(); i++ ) { - bool matched = true; - for ( int j=0; jcurr_level, (int)level ); - CRLOG->curr_level = level; -} - -CRLog::log_level CRLog::getLogLevel() -{ - if ( !CRLOG ) - return LL_INFO; - return CRLOG->curr_level; -} - -bool CRLog::isLogLevelEnabled( CRLog::log_level level ) -{ - if ( !CRLOG ) - return false; - return (CRLOG->curr_level >= level); -} - -void CRLog::fatal( const char * msg, ... ) -{ - if ( !CRLOG ) - return; - va_list args; - va_start( args, msg ); - CRLOG->log( "FATAL", msg, args ); - va_end(args); -} - -void CRLog::error( const char * msg, ... ) -{ - if ( !CRLOG || CRLOG->curr_levellog( "ERROR", msg, args ); - va_end(args); -} - -void CRLog::warn( const char * msg, ... ) -{ - if ( !CRLOG || CRLOG->curr_levellog( "WARN", msg, args ); - va_end(args); -} - -void CRLog::info( const char * msg, ... ) -{ - if ( !CRLOG || CRLOG->curr_levellog( "INFO", msg, args ); - va_end(args); -} - -void CRLog::debug( const char * msg, ... ) -{ - if ( !CRLOG || CRLOG->curr_levellog( "DEBUG", msg, args ); - va_end(args); -} - -void CRLog::trace( const char * msg, ... ) -{ - if ( !CRLOG || CRLOG->curr_levellog( "TRACE", msg, args ); - va_end(args); -} - -CRLog::CRLog() - : curr_level(LL_INFO) -{ -} - -CRLog::~CRLog() -{ -} - -#ifndef LOG_HEAP_USAGE -#define LOG_HEAP_USAGE 0 -#endif - -class CRFileLogger : public CRLog -{ -protected: - FILE * f; - bool autoClose; - bool autoFlush; - virtual void log( const char * level, const char * msg, va_list args ) - { - if ( !f ) - return; -#ifdef LINUX - struct timeval tval; - gettimeofday( &tval, NULL ); - int ms = tval.tv_usec; - time_t t = tval.tv_sec; -#if LOG_HEAP_USAGE - struct mallinfo mi = mallinfo(); - int memusage = mi.arena; -#endif -#else - lUInt64 ts = GetCurrentTimeMillis(); - //time_t t = (time_t)time(0); - time_t t = ts / 1000; - int ms = (ts % 1000) * 1000; -#if LOG_HEAP_USAGE - int memusage = 0; -#endif -#endif - tm * bt = localtime(&t); -#if LOG_HEAP_USAGE - fprintf(f, "%04d/%02d/%02d %02d:%02d:%02d.%04d [%d] %s ", bt->tm_year+1900, bt->tm_mon+1, bt->tm_mday, bt->tm_hour, bt->tm_min, bt->tm_sec, ms/100, memusage, level); -#else - fprintf(f, "%04d/%02d/%02d %02d:%02d:%02d.%04d %s ", bt->tm_year+1900, bt->tm_mon+1, bt->tm_mday, bt->tm_hour, bt->tm_min, bt->tm_sec, ms/100, level); -#endif - vfprintf( f, msg, args ); - fprintf(f, "\n" ); - if ( autoFlush ) - fflush( f ); - } -public: - CRFileLogger( FILE * file, bool _autoClose, bool _autoFlush ) - : f(file), autoClose(_autoClose), autoFlush( _autoFlush ) - { - info( "Started logging" ); - } - - CRFileLogger( const char * fname, bool _autoFlush ) - : f(fopen( fname, "wt" )), autoClose(true), autoFlush( _autoFlush ) - { - static unsigned char utf8sign[] = {0xEF, 0xBB, 0xBF}; - static const char * log_level_names[] = { - "FATAL", - "ERROR", - "WARN", - "INFO", - "DEBUG", - "TRACE", - }; - fwrite( utf8sign, 3, 1, f); - info( "Started logging. Level=%s", log_level_names[getLogLevel()] ); - } - - virtual ~CRFileLogger() { - if ( f && autoClose ) { - info( "Stopped logging" ); - fclose( f ); - } - f = NULL; - } -}; - -void CRLog::setFileLogger( const char * fname, bool autoFlush ) -{ - setLogger( new CRFileLogger( fname, autoFlush ) ); -} - -void CRLog::setStdoutLogger() -{ - setLogger( new CRFileLogger( (FILE*)stdout, false, true ) ); -} - -void CRLog::setStderrLogger() -{ - setLogger( new CRFileLogger( (FILE*)stderr, false, true ) ); -} - /// returns true if string starts with specified substring, case insensitive bool lString16::startsWithNoCase ( const lString16 & substring ) const { @@ -4841,287 +4239,6 @@ bool lString16::startsWith(const lChar8 * substring) const return true; } - - -/// serialization/deserialization buffer - -/// constructor of serialization buffer -SerialBuf::SerialBuf( int sz, bool autoresize ) - : _buf( (lUInt8*)malloc(sz) ), _ownbuf(true), _error(false), _autoresize(autoresize), _size(sz), _pos(0) -{ - memset( _buf, 0, _size ); -} -/// constructor of deserialization buffer -SerialBuf::SerialBuf( const lUInt8 * p, int sz ) - : _buf( const_cast(p) ), _ownbuf(false), _error(false), _autoresize(false), _size(sz), _pos(0) -{ -} - -SerialBuf::~SerialBuf() -{ - if ( _ownbuf ) - free( _buf ); -} - -bool SerialBuf::copyTo( lUInt8 * buf, int maxSize ) -{ - if ( _pos==0 ) - return true; - if ( _pos > maxSize ) - return false; - memcpy( buf, _buf, _pos ); - return true; -} - -/// checks whether specified number of bytes is available, returns true in case of error -bool SerialBuf::check( int reserved ) -{ - if ( _error ) - return true; - if ( space()16384 ? _size*2 : 16384) + reserved; - _buf = cr_realloc(_buf, _size ); - memset( _buf+_pos, 0, _size-_pos ); - return false; - } else { - _error = true; - return true; - } - } - return false; -} - -// write methods -/// put magic signature -void SerialBuf::putMagic( const char * s ) -{ - if ( check(1) ) - return; - while ( *s ) { - _buf[ _pos++ ] = *s++; - if ( check(1) ) - return; - } -} - -#define SWAPVARS(t,a) \ -{ \ - t tmp; \ - tmp = a; a = v.a; v.a = tmp; \ -} -void SerialBuf::swap( SerialBuf & v ) -{ - SWAPVARS(lUInt8 *, _buf) - SWAPVARS(bool, _ownbuf) - SWAPVARS(bool, _error) - SWAPVARS(bool, _autoresize) - SWAPVARS(int, _size) - SWAPVARS(int, _pos) -} - - -/// add contents of another buffer -SerialBuf & SerialBuf::operator << ( const SerialBuf & v ) -{ - if ( check(v.pos()) || v.pos()==0 ) - return *this; - memcpy( _buf + _pos, v._buf, v._pos ); - _pos += v._pos; - return *this; -} - -SerialBuf & SerialBuf::operator << ( lUInt8 n ) -{ - if ( check(1) ) - return *this; - _buf[_pos++] = n; - return *this; -} -SerialBuf & SerialBuf::operator << ( char n ) -{ - if ( check(1) ) - return *this; - _buf[_pos++] = (lUInt8)n; - return *this; -} -SerialBuf & SerialBuf::operator << ( bool n ) -{ - if ( check(1) ) - return *this; - _buf[_pos++] = (lUInt8)(n ? 1 : 0); - return *this; -} -SerialBuf & SerialBuf::operator << ( lUInt16 n ) -{ - if ( check(2) ) - return *this; - _buf[_pos++] = (lUInt8)(n & 255); - _buf[_pos++] = (lUInt8)((n>>8) & 255); - return *this; -} -SerialBuf & SerialBuf::operator << ( lInt16 n ) -{ - if ( check(2) ) - return *this; - _buf[_pos++] = (lUInt8)(n & 255); - _buf[_pos++] = (lUInt8)((n>>8) & 255); - return *this; -} -SerialBuf & SerialBuf::operator << ( lUInt32 n ) -{ - if ( check(4) ) - return *this; - _buf[_pos++] = (lUInt8)(n & 255); - _buf[_pos++] = (lUInt8)((n>>8) & 255); - _buf[_pos++] = (lUInt8)((n>>16) & 255); - _buf[_pos++] = (lUInt8)((n>>24) & 255); - return *this; -} -SerialBuf & SerialBuf::operator << ( lInt32 n ) -{ - if ( check(4) ) - return *this; - _buf[_pos++] = (lUInt8)(n & 255); - _buf[_pos++] = (lUInt8)((n>>8) & 255); - _buf[_pos++] = (lUInt8)((n>>16) & 255); - _buf[_pos++] = (lUInt8)((n>>24) & 255); - return *this; -} -SerialBuf & SerialBuf::operator << ( const lString16 & s ) -{ - if ( check(2) ) - return *this; - lString8 s8 = UnicodeToUtf8(s); - lUInt16 len = (lUInt16)s8.length(); - (*this) << len; - for ( int i=0; i> ( lUInt8 & n ) -{ - if ( check(1) ) - return *this; - n = _buf[_pos++]; - return *this; -} - -SerialBuf & SerialBuf::operator >> ( char & n ) -{ - if ( check(1) ) - return *this; - n = (char)_buf[_pos++]; - return *this; -} - -SerialBuf & SerialBuf::operator >> ( bool & n ) -{ - if ( check(1) ) - return *this; - n = _buf[_pos++] ? true : false; - return *this; -} - -SerialBuf & SerialBuf::operator >> ( lUInt16 & n ) -{ - if ( check(2) ) - return *this; - n = _buf[_pos++]; - n |= (((lUInt16)_buf[_pos++]) << 8); - return *this; -} - -SerialBuf & SerialBuf::operator >> ( lInt16 & n ) -{ - if ( check(2) ) - return *this; - n = (lInt16)(_buf[_pos++]); - n |= (lInt16)(((lUInt16)_buf[_pos++]) << 8); - return *this; -} - -SerialBuf & SerialBuf::operator >> ( lUInt32 & n ) -{ - if ( check(4) ) - return *this; - n = _buf[_pos++]; - n |= (((lUInt32)_buf[_pos++]) << 8); - n |= (((lUInt32)_buf[_pos++]) << 16); - n |= (((lUInt32)_buf[_pos++]) << 24); - return *this; -} - -SerialBuf & SerialBuf::operator >> ( lInt32 & n ) -{ - if ( check(4) ) - return *this; - n = (lInt32)(_buf[_pos++]); - n |= (((lUInt32)_buf[_pos++]) << 8); - n |= (((lUInt32)_buf[_pos++]) << 16); - n |= (((lUInt32)_buf[_pos++]) << 24); - return *this; -} - -SerialBuf & SerialBuf::operator >> ( lString8 & s8 ) -{ - if ( check(2) ) - return *this; - lUInt16 len = 0; - (*this) >> len; - s8.clear(); - s8.reserve(len); - for ( int i=0; i> c; - s8.append(1, c); - } - return *this; -} - -SerialBuf & SerialBuf::operator >> ( lString16 & s ) -{ - lString8 s8; - (*this) >> s8; - s = Utf8ToUnicode(s8); - return *this; -} - -// read methods -bool SerialBuf::checkMagic( const char * s ) -{ - if ( _error ) - return false; - while ( *s ) { - if ( check(1) ) - return false; - if ( _buf[ _pos++ ] != *s++ ) { - seterror(); - return false; - } - } - return true; -} - bool lString16::split2( const lString16 & delim, lString16 & value1, lString16 & value2 ) { if ( empty() ) @@ -5290,53 +4407,3 @@ void limitStringSize(lString16 & str, int maxSize) { str = str.substr(0, split); str += "..."; } - - -#ifdef _WIN32 -static bool __timerInitialized = false; -static double __timeTicksPerMillis; -static lUInt64 __timeStart; -static lUInt64 __timeAbsolute; -static lUInt64 __startTimeMillis; -#endif - -void CRReinitTimer() { -#ifdef _WIN32 - LARGE_INTEGER tps; - QueryPerformanceFrequency(&tps); - __timeTicksPerMillis = (double)(tps.QuadPart / 1000L); - LARGE_INTEGER queryTime; - QueryPerformanceCounter(&queryTime); - __timeStart = (lUInt64)(queryTime.QuadPart / __timeTicksPerMillis); - __timerInitialized = true; - FILETIME ft; - GetSystemTimeAsFileTime(&ft); - __startTimeMillis = (ft.dwLowDateTime | (((lUInt64)ft.dwHighDateTime) << 32)) / 10000; -#else - // do nothing. it's for win32 only -#endif -} - - -lUInt64 GetCurrentTimeMillis() { -#if defined(LINUX) || defined(ANDROID) || defined(_LINUX) - timeval ts; - gettimeofday(&ts, NULL); - return ts.tv_sec * (lUInt64)1000 + ts.tv_usec / 1000; -#else - #ifdef _WIN32 - if (!__timerInitialized) { - CRReinitTimer(); - return __startTimeMillis; - } else { - LARGE_INTEGER queryTime; - QueryPerformanceCounter(&queryTime); - __timeAbsolute = (lUInt64)(queryTime.QuadPart / __timeTicksPerMillis); - return __startTimeMillis + (lUInt64)(__timeAbsolute - __timeStart); - } - #else - #error * You should define GetCurrentTimeMillis() * - #endif -#endif -} - diff --git a/crengine/src/lvstring16collection.cpp b/crengine/src/lvstring16collection.cpp new file mode 100644 index 0000000000..69a8c0c661 --- /dev/null +++ b/crengine/src/lvstring16collection.cpp @@ -0,0 +1,155 @@ +/******************************************************* + + CoolReader Engine + + lvstring16collection.cpp: collection of wide strings + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License + See LICENSE file for details + +*******************************************************/ + +#include "../include/lvstring16collection.h" + +void lString16Collection::reserve(int space) +{ + if ( count + space > size ) + { + int tmpSize = count + space + 64; + void* tmp = realloc( chunks, sizeof(lstring16_chunk_t *) * tmpSize ); + if (tmp) { + size = tmpSize; + chunks = (lstring16_chunk_t * *)tmp; + } + else { + // TODO: throw exception or change function prototype & return code + } + } +} + +static int (str16_comparator)(const void * n1, const void * n2) +{ + lstring16_chunk_t ** s1 = (lstring16_chunk_t **)n1; + lstring16_chunk_t ** s2 = (lstring16_chunk_t **)n2; + return lStr_cmp( (*s1)->data16(), (*s2)->data16() ); +} + +static int(*custom_lstr16_comparator_ptr)(lString16 & s1, lString16 & s2); +static int (str16_custom_comparator)(const void * n1, const void * n2) +{ + lString16 s1(*((lstring16_chunk_t **)n1)); + lString16 s2(*((lstring16_chunk_t **)n2)); + return custom_lstr16_comparator_ptr(s1, s2); +} + +void lString16Collection::sort(int(comparator)(lString16 & s1, lString16 & s2)) +{ + custom_lstr16_comparator_ptr = comparator; + qsort(chunks,count,sizeof(lstring16_chunk_t*), str16_custom_comparator); +} + +void lString16Collection::sort() +{ + qsort(chunks,count,sizeof(lstring16_chunk_t*), str16_comparator); +} + +int lString16Collection::add( const lString16 & str ) +{ + reserve( 1 ); + chunks[count] = str.pchunk; + str.addref(); + return count++; +} +void lString16Collection::clear() +{ + if (chunks) { + for (int i=0; i= count) + return; + int i; + for (i = offset; i < offset + cnt; i++) + { + ((lString16 *)chunks)[i].release(); + } + for (i = offset + cnt; i < count; i++) + { + chunks[i-cnt] = chunks[i]; + } + count -= cnt; + if (!count) + clear(); +} + +void lString16Collection::split( const lString16 & str, const lString16 & delimiter ) +{ + if (str.empty()) + return; + for (int startpos = 0; startpos < str.length(); ) { + int pos = str.pos(delimiter, startpos); + if (pos < 0) + pos = str.length(); + add(str.substr(startpos, pos - startpos)); + startpos = pos + delimiter.length(); + } +} + +void lString16Collection::parse( lString16 string, lChar16 delimiter, bool flgTrim ) +{ + int wstart=0; + for ( int i=0; i<=string.length(); i++ ) { + if ( i==string.length() || string[i]==delimiter ) { + lString16 s( string.substr( wstart, i-wstart) ); + if ( flgTrim ) + s.trimDoubleSpaces(false, false, false); + if ( !flgTrim || !s.empty() ) + add( s ); + wstart = i+1; + } + } +} + +void lString16Collection::parse( lString16 string, lString16 delimiter, bool flgTrim ) +{ + if ( delimiter.empty() || string.pos(delimiter)<0 ) { + lString16 s( string ); + if ( flgTrim ) + s.trimDoubleSpaces(false, false, false); + add(s); + return; + } + int wstart=0; + for ( int i=0; i<=string.length(); i++ ) { + bool matched = true; + for ( int j=0; j> count; + for ( int i=0; i> s; + if ( buf.error() ) + break; + add( s.c_str() ); + } + buf.checkCRC( buf.pos() - start ); + return !buf.error(); +} + +lString16HashedCollection::lString16HashedCollection( lString16HashedCollection & v ) +: lString16Collection( v ) +, hashSize( v.hashSize ) +, hash( NULL ) +{ + hash = (HashPair *)malloc( sizeof(HashPair) * hashSize ); + for ( int i=0; iindex ); + next = next->next; + } + } +} + +void lString16HashedCollection::addHashItem( int hashIndex, int storageIndex ) +{ + if ( hash[ hashIndex ].index == -1 ) { + hash[hashIndex].index = storageIndex; + } else { + HashPair * np = (HashPair *)malloc(sizeof(HashPair)); + np->index = storageIndex; + np->next = hash[hashIndex].next; + hash[hashIndex].next = np; + } +} + +void lString16HashedCollection::clearHash() +{ + if ( hash ) { + for ( int i=0; inext; + free( p ); + p = tmp; + } + } + free( hash ); + } + hash = NULL; +} + +lString16HashedCollection::lString16HashedCollection( lUInt32 hash_size ) +: hashSize(hash_size), hash(NULL) +{ + + hash = (HashPair *)malloc( sizeof(HashPair) * hashSize ); + for ( int i=0; inext ) { + const lString16 & str = at( p->index ); + if ( str==s ) + return p->index; + } + } + return -1; +} + +void lString16HashedCollection::reHash( int newSize ) +{ + if (hashSize == newSize) + return; + clearHash(); + hashSize = newSize; + if (hashSize > 0) { + hash = (HashPair *)malloc( sizeof(HashPair) * hashSize ); + for ( int i=0; inext ) { + const lString16 & str = at( p->index ); + if ( str==s ) + return p->index; + } + } + lUInt32 i = lString16Collection::add( lString16(s) ); + addHashItem( n, i ); + return i; +} diff --git a/crengine/src/lvstring8collection.cpp b/crengine/src/lvstring8collection.cpp new file mode 100644 index 0000000000..806bdb1025 --- /dev/null +++ b/crengine/src/lvstring8collection.cpp @@ -0,0 +1,83 @@ +/******************************************************* + + CoolReader Engine + + lvstring16collection.cpp: collection of strings + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License + See LICENSE file for details + +*******************************************************/ + +#include "../include/lvstring8collection.h" + +void lString8Collection::split( const lString8 & str, const lString8 & delimiter ) +{ + if (str.empty()) + return; + for (int startpos = 0; startpos < str.length(); ) { + int pos = str.pos(delimiter, startpos); + if (pos < 0) + pos = str.length(); + add(str.substr(startpos, pos - startpos)); + startpos = pos + delimiter.length(); + } +} + +void lString8Collection::erase(int offset, int cnt) +{ + if (count <= 0) + return; + if (offset < 0 || offset + cnt > count) + return; + int i; + for (i = offset; i < offset + cnt; i++) + { + ((lString8 *)chunks)[i].release(); + } + for (i = offset + cnt; i < count; i++) + { + chunks[i-cnt] = chunks[i]; + } + count -= cnt; + if (!count) + clear(); +} + +void lString8Collection::reserve(int space) +{ + if ( count + space > size ) + { + int tmpSize = count + space + 64; + void* tmp = realloc( chunks, sizeof(lstring8_chunk_t *) * tmpSize ); + if (tmp) { + size = tmpSize; + chunks = (lstring8_chunk_t * *)tmp; + } + else { + // TODO: throw exception or change function prototype & return code + } + } +} + +int lString8Collection::add( const lString8 & str ) +{ + reserve( 1 ); + chunks[count] = str.pchunk; + str.addref(); + return count++; +} +void lString8Collection::clear() +{ + for (int i=0; i #include #include diff --git a/crengine/src/pdbfmt.cpp b/crengine/src/pdbfmt.cpp index f0afb1c81a..72184822a1 100644 --- a/crengine/src/pdbfmt.cpp +++ b/crengine/src/pdbfmt.cpp @@ -1,4 +1,5 @@ #include "../include/pdbfmt.h" +#include "../include/crlog.h" #include // uncomment following line to save PDB content streams to /tmp diff --git a/crengine/src/private/dumpfile.h b/crengine/src/private/dumpfile.h new file mode 100644 index 0000000000..ef2f365191 --- /dev/null +++ b/crengine/src/private/dumpfile.h @@ -0,0 +1,40 @@ +/** \file dumpfile.h + \brief dump file interface + + CoolReader Engine + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License. + + See LICENSE file for details. +*/ + +#ifndef __DUMPFILE_H_INCLUDED__ +#define __DUMPFILE_H_INCLUDED__ + +#ifdef _DEBUG +#include +class DumpFile +{ +public: + FILE * f; + DumpFile( const char * fname ) + : f(NULL) + { + if ( fname ) + f = fopen( fname, "at" ); + if ( !f ) + f = stdout; + fprintf(f, "DumpFile log started\n"); + } + ~DumpFile() + { + if ( f!=stdout ) + fclose(f); + } + operator FILE * () { if (f) fflush(f); return f?f:stdout; } +}; +#endif + +#endif // __DUMPFILE_H_INCLUDED__ diff --git a/crengine/src/private/lvfontcache.cpp b/crengine/src/private/lvfontcache.cpp index 261a95a05f..b74125b13d 100644 --- a/crengine/src/private/lvfontcache.cpp +++ b/crengine/src/private/lvfontcache.cpp @@ -14,6 +14,7 @@ #include "lvfontcache.h" #include "../../include/lvstyles.h" +#include "../../include/crlog.h" LVFontCacheItem *LVFontCache::findDuplicate(const LVFontDef *def) { diff --git a/crengine/src/private/lvfontcache.h b/crengine/src/private/lvfontcache.h index a1ea5d37c8..0f191b3d24 100644 --- a/crengine/src/private/lvfontcache.h +++ b/crengine/src/private/lvfontcache.h @@ -18,6 +18,7 @@ #include "../../include/crsetup.h" #include "../../include/lvfont.h" #include "../../include/lvptrvec.h" +#include "../../include/lvstring16collection.h" #include "lvfontdef.h" /// font cache item diff --git a/crengine/src/private/lvfreetypeface.cpp b/crengine/src/private/lvfreetypeface.cpp index e808fd8a0a..f6f9db0bd4 100644 --- a/crengine/src/private/lvfreetypeface.cpp +++ b/crengine/src/private/lvfreetypeface.cpp @@ -18,6 +18,7 @@ #include "../../include/lvfntman.h" #include "../../include/lvfnt.h" #include "../../include/lvtextfm.h" +#include "../../include/crlog.h" #include "lvfontglyphcache.h" #include "lvfontdef.h" #include "lvfontcache.h" diff --git a/crengine/src/private/lvfreetypefontman.cpp b/crengine/src/private/lvfreetypefontman.cpp index 311da9a4c9..e571c8d436 100644 --- a/crengine/src/private/lvfreetypefontman.cpp +++ b/crengine/src/private/lvfreetypefontman.cpp @@ -1,4 +1,4 @@ -/** \file lvfreetypefontman.c +/** \file lvfreetypefontman.cpp \brief FreeType font manager implementation CoolReader Engine @@ -15,6 +15,7 @@ #include "lvfreetypefontman.h" #include "lvfreetypeface.h" #include "lvfontboldtransform.h" +#include "../../include/crlog.h" #if (USE_FONTCONFIG == 1) #include diff --git a/crengine/src/props.cpp b/crengine/src/props.cpp index 1ae8253020..84140a98cf 100644 --- a/crengine/src/props.cpp +++ b/crengine/src/props.cpp @@ -10,6 +10,7 @@ */ #include "../include/props.h" +#include "../include/serialbuf.h" #include diff --git a/crengine/src/serialbuf.cpp b/crengine/src/serialbuf.cpp new file mode 100644 index 0000000000..8cfd3b0ed1 --- /dev/null +++ b/crengine/src/serialbuf.cpp @@ -0,0 +1,337 @@ +/******************************************************* + + CoolReader Engine + + serialbuf.cpp: serialization buffer class implementation + + (c) Vadim Lopatin, 2000-2006 + This source code is distributed under the terms of + GNU General Public License + See LICENSE file for details + +*******************************************************/ + +#include "../include/serialbuf.h" + +/// serialization/deserialization buffer + +/// constructor of serialization buffer +SerialBuf::SerialBuf( int sz, bool autoresize ) + : _buf( (lUInt8*)malloc(sz) ), _ownbuf(true), _error(false), _autoresize(autoresize), _size(sz), _pos(0) +{ + memset( _buf, 0, _size ); +} +/// constructor of deserialization buffer +SerialBuf::SerialBuf( const lUInt8 * p, int sz ) + : _buf( const_cast(p) ), _ownbuf(false), _error(false), _autoresize(false), _size(sz), _pos(0) +{ +} + +SerialBuf::~SerialBuf() +{ + if ( _ownbuf ) + free( _buf ); +} + +bool SerialBuf::copyTo( lUInt8 * buf, int maxSize ) +{ + if ( _pos==0 ) + return true; + if ( _pos > maxSize ) + return false; + memcpy( buf, _buf, _pos ); + return true; +} + +/// checks whether specified number of bytes is available, returns true in case of error +bool SerialBuf::check( int reserved ) +{ + if ( _error ) + return true; + if ( space()16384 ? _size*2 : 16384) + reserved; + _buf = cr_realloc(_buf, _size ); + memset( _buf+_pos, 0, _size-_pos ); + return false; + } else { + _error = true; + return true; + } + } + return false; +} + +// write methods +/// put magic signature +void SerialBuf::putMagic( const char * s ) +{ + if ( check(1) ) + return; + while ( *s ) { + _buf[ _pos++ ] = *s++; + if ( check(1) ) + return; + } +} + +#define SWAPVARS(t,a) \ +{ \ + t tmp; \ + tmp = a; a = v.a; v.a = tmp; \ +} +void SerialBuf::swap( SerialBuf & v ) +{ + SWAPVARS(lUInt8 *, _buf) + SWAPVARS(bool, _ownbuf) + SWAPVARS(bool, _error) + SWAPVARS(bool, _autoresize) + SWAPVARS(int, _size) + SWAPVARS(int, _pos) +} + + +/// add contents of another buffer +SerialBuf & SerialBuf::operator << ( const SerialBuf & v ) +{ + if ( check(v.pos()) || v.pos()==0 ) + return *this; + memcpy( _buf + _pos, v._buf, v._pos ); + _pos += v._pos; + return *this; +} + +SerialBuf & SerialBuf::operator << ( lUInt8 n ) +{ + if ( check(1) ) + return *this; + _buf[_pos++] = n; + return *this; +} +SerialBuf & SerialBuf::operator << ( char n ) +{ + if ( check(1) ) + return *this; + _buf[_pos++] = (lUInt8)n; + return *this; +} +SerialBuf & SerialBuf::operator << ( bool n ) +{ + if ( check(1) ) + return *this; + _buf[_pos++] = (lUInt8)(n ? 1 : 0); + return *this; +} +SerialBuf & SerialBuf::operator << ( lUInt16 n ) +{ + if ( check(2) ) + return *this; + _buf[_pos++] = (lUInt8)(n & 255); + _buf[_pos++] = (lUInt8)((n>>8) & 255); + return *this; +} +SerialBuf & SerialBuf::operator << ( lInt16 n ) +{ + if ( check(2) ) + return *this; + _buf[_pos++] = (lUInt8)(n & 255); + _buf[_pos++] = (lUInt8)((n>>8) & 255); + return *this; +} +SerialBuf & SerialBuf::operator << ( lUInt32 n ) +{ + if ( check(4) ) + return *this; + _buf[_pos++] = (lUInt8)(n & 255); + _buf[_pos++] = (lUInt8)((n>>8) & 255); + _buf[_pos++] = (lUInt8)((n>>16) & 255); + _buf[_pos++] = (lUInt8)((n>>24) & 255); + return *this; +} +SerialBuf & SerialBuf::operator << ( lInt32 n ) +{ + if ( check(4) ) + return *this; + _buf[_pos++] = (lUInt8)(n & 255); + _buf[_pos++] = (lUInt8)((n>>8) & 255); + _buf[_pos++] = (lUInt8)((n>>16) & 255); + _buf[_pos++] = (lUInt8)((n>>24) & 255); + return *this; +} +SerialBuf & SerialBuf::operator << ( const lString16 & s ) +{ + if ( check(2) ) + return *this; + lString8 s8 = UnicodeToUtf8(s); + lUInt16 len = (lUInt16)s8.length(); + (*this) << len; + for ( int i=0; i> ( lUInt8 & n ) +{ + if ( check(1) ) + return *this; + n = _buf[_pos++]; + return *this; +} + +SerialBuf & SerialBuf::operator >> ( char & n ) +{ + if ( check(1) ) + return *this; + n = (char)_buf[_pos++]; + return *this; +} + +SerialBuf & SerialBuf::operator >> ( bool & n ) +{ + if ( check(1) ) + return *this; + n = _buf[_pos++] ? true : false; + return *this; +} + +SerialBuf & SerialBuf::operator >> ( lUInt16 & n ) +{ + if ( check(2) ) + return *this; + n = _buf[_pos++]; + n |= (((lUInt16)_buf[_pos++]) << 8); + return *this; +} + +SerialBuf & SerialBuf::operator >> ( lInt16 & n ) +{ + if ( check(2) ) + return *this; + n = (lInt16)(_buf[_pos++]); + n |= (lInt16)(((lUInt16)_buf[_pos++]) << 8); + return *this; +} + +SerialBuf & SerialBuf::operator >> ( lUInt32 & n ) +{ + if ( check(4) ) + return *this; + n = _buf[_pos++]; + n |= (((lUInt32)_buf[_pos++]) << 8); + n |= (((lUInt32)_buf[_pos++]) << 16); + n |= (((lUInt32)_buf[_pos++]) << 24); + return *this; +} + +SerialBuf & SerialBuf::operator >> ( lInt32 & n ) +{ + if ( check(4) ) + return *this; + n = (lInt32)(_buf[_pos++]); + n |= (((lUInt32)_buf[_pos++]) << 8); + n |= (((lUInt32)_buf[_pos++]) << 16); + n |= (((lUInt32)_buf[_pos++]) << 24); + return *this; +} + +SerialBuf & SerialBuf::operator >> ( lString8 & s8 ) +{ + if ( check(2) ) + return *this; + lUInt16 len = 0; + (*this) >> len; + s8.clear(); + s8.reserve(len); + for ( int i=0; i> c; + s8.append(1, c); + } + return *this; +} + +SerialBuf & SerialBuf::operator >> ( lString16 & s ) +{ + lString8 s8; + (*this) >> s8; + s = Utf8ToUnicode(s8); + return *this; +} + +// read methods +bool SerialBuf::checkMagic( const char * s ) +{ + if ( _error ) + return false; + while ( *s ) { + if ( check(1) ) + return false; + if ( _buf[ _pos++ ] != *s++ ) { + seterror(); + return false; + } + } + return true; +} + +/// add CRC32 for last N bytes +void SerialBuf::putCRC( int size ) +{ + if ( error() ) + return; + if ( size>_pos ) { + *this << (lUInt32)0; + seterror(); + } + lUInt32 n = 0; + n = lStr_crc32( n, _buf + _pos-size, size ); + *this << n; +} + +/// get CRC32 for the whole buffer +lUInt32 SerialBuf::getCRC() +{ + if (error()) + return 0; + lUInt32 n = 0; + n = lStr_crc32( n, _buf, _pos ); + return n; +} + +/// read crc32 code, comapare with CRC32 for last N bytes +bool SerialBuf::checkCRC( int size ) +{ + if ( error() ) + return false; + if ( size>_pos ) { + seterror(); + return false; + } + lUInt32 n0 = 0; + n0 = lStr_crc32(n0, _buf + _pos-size, size); + lUInt32 n = 0; + *this >> n; + if ( error() ) + return false; + if ( n!=n0 ) + seterror(); + return !error(); +} diff --git a/crengine/src/txtselector.cpp b/crengine/src/txtselector.cpp index 8c44d6c90c..894e3ccca8 100644 --- a/crengine/src/txtselector.cpp +++ b/crengine/src/txtselector.cpp @@ -12,6 +12,7 @@ *******************************************************/ #include "../include/txtselector.h" +#include "../include/crlog.h" /// create selection tool for specified initial range (usually current page) ldomTextSelectionTool::ldomTextSelectionTool( ldomXRange & initialRange, ldomTextSelectionTool::interval_t initialInterval, ldomTextSelectionTool::origin_t initialOrigin ) diff --git a/crengine/src/wolutil.cpp b/crengine/src/wolutil.cpp index fb31f785d7..83a0a93004 100644 --- a/crengine/src/wolutil.cpp +++ b/crengine/src/wolutil.cpp @@ -10,7 +10,7 @@ #include #include "../include/wolutil.h" - +#include "private/dumpfile.h" #define N 4096 #define F 18 diff --git a/crengine/src/wordfmt.cpp b/crengine/src/wordfmt.cpp index 5a76049263..587524f7bc 100644 --- a/crengine/src/wordfmt.cpp +++ b/crengine/src/wordfmt.cpp @@ -2,6 +2,7 @@ #include "../include/lvstring.h" #include "../include/lvstream.h" #include "../include/lvtinydom.h" +#include "../include/crlog.h" //#ifndef ENABLE_ANTIWORD //#define ENABLE_ANTIWORD 1 From 84b13621c59d7f28799302d2f911e93ad2cc0435 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Wed, 25 Dec 2019 00:42:09 +0400 Subject: [PATCH 7/8] To resolve unhandled exception 'JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal continuation byte 0' that can be produced by invalid filename (broken sdcard, etc) or 'JNI WARNING: input is not valid Modified UTF-8: illegal start byte 0xf0' that can be generated if 4-byte UTF-8 sequence found in the filename on Android older than 6.0 (API 23), we implement own directory listing method instead of File.listFiles(). --- android/jni/cr3engine.cpp | 42 +++++++++++++++++++ android/jni/cr3java.cpp | 3 -- android/jni/cr3java.h | 3 ++ android/jni/org_coolreader_crengine_Engine.h | 10 ++++- .../src/org/coolreader/crengine/Engine.java | 6 +++ .../src/org/coolreader/crengine/Scanner.java | 10 ++++- 6 files changed, 69 insertions(+), 5 deletions(-) diff --git a/android/jni/cr3engine.cpp b/android/jni/cr3engine.cpp index 6dc2f70d0e..62358aa5c3 100644 --- a/android/jni/cr3engine.cpp +++ b/android/jni/cr3engine.cpp @@ -709,6 +709,45 @@ JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_checkFontLanguage return res; } +/* + * Class: org_coolreader_crengine_Engine + * Method: listFilesInternal + * Signature: (Ljava/io/File;)[Ljava/io/File; + */ +JNIEXPORT jobjectArray JNICALL Java_org_coolreader_crengine_Engine_listFilesInternal + (JNIEnv *env, jclass, jobject jdir) +{ + jclass fileClass = env->FindClass("java/io/File"); + if (NULL == fileClass) + return NULL; + jmethodID mFileGetAbsolutePath = env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;"); + if (NULL == mFileGetAbsolutePath) + return NULL; + jmethodID fileCtor = env->GetMethodID(fileClass, "", "(Ljava/lang/String;)V"); + if (NULL == fileCtor) + return NULL; + jstring jpathname = (jstring)env->CallObjectMethod(jdir, mFileGetAbsolutePath); + jboolean iscopy; + const char * s = env->GetStringUTFChars(jpathname, &iscopy); + lString16 path = (CRJNIEnv::sdk_int >= ANDROID_SDK_M) ? Utf8ToUnicode(s) : Wtf8ToUnicode(s); + LVContainerRef dir = LVOpenDirectory(path); + jobjectArray jarray = NULL; + if ( !dir.isNull() ) { + jstring emptyString = env->NewStringUTF(""); + jobject emptyFile = env->NewObject(fileClass, fileCtor, emptyString); + jarray = env->NewObjectArray(dir->GetObjectCount(), fileClass, emptyFile); + for (int i = 0; i < dir->GetObjectCount(); i++) { + const LVContainerItemInfo *item = dir->GetObjectInfo(i); + lString16 fileName = path + "/" + item->GetName(); + jstring jfilename = env->NewStringUTF((CRJNIEnv::sdk_int >= ANDROID_SDK_M) ? UnicodeToUtf8(fileName).c_str() : UnicodeToWtf8(fileName).c_str()); + jobject jfile = env->NewObject(fileClass, fileCtor, jfilename); + env->SetObjectArrayElement(jarray, i, jfile); + } + } + env->ReleaseStringUTFChars(jpathname, s); + return jarray; +} + /* * Class: org_coolreader_crengine_Engine * Method: isLink @@ -789,6 +828,9 @@ static JNINativeMethod sEngineMethods[] = { {"setKeyBacklightInternal", "(I)Z", (void*)Java_org_coolreader_crengine_Engine_setKeyBacklightInternal}, {"scanBookCoverInternal", "(Ljava/lang/String;)[B", (void*)Java_org_coolreader_crengine_Engine_scanBookCoverInternal}, {"drawBookCoverInternal", "(Landroid/graphics/Bitmap;[BLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)V", (void*)Java_org_coolreader_crengine_Engine_drawBookCoverInternal}, + {"haveFcLangCodeInternal", "(Ljava/lang/String;)Z", (void*)Java_org_coolreader_crengine_Engine_haveFcLangCodeInternal}, + {"checkFontLanguageCompatibilityInternal", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)Java_org_coolreader_crengine_Engine_checkFontLanguageCompatibilityInternal}, + {"listFilesInternal", "(Ljava/io/File;)[Ljava/io/File;", (void*)Java_org_coolreader_crengine_Engine_listFilesInternal} }; diff --git a/android/jni/cr3java.cpp b/android/jni/cr3java.cpp index 35776e91f4..a0276b8389 100644 --- a/android/jni/cr3java.cpp +++ b/android/jni/cr3java.cpp @@ -3,9 +3,6 @@ #include -// M is for Marshmallow! -#define ANDROID_SDK_M 23 - uint8_t CRJNIEnv::sdk_int = 0; lString16 CRJNIEnv::fromJavaString( jstring str ) diff --git a/android/jni/cr3java.h b/android/jni/cr3java.h index 43029b0bf8..b0bf89eb01 100644 --- a/android/jni/cr3java.h +++ b/android/jni/cr3java.h @@ -20,6 +20,9 @@ #include "../../crengine/include/props.h" #include "../../crengine/include/lvtinydom.h" +// M is for Marshmallow! +#define ANDROID_SDK_M 23 + //==================================================================== // libjnigraphics replacement for pre-2.2 SDKs enum AndroidBitmapFormat { diff --git a/android/jni/org_coolreader_crengine_Engine.h b/android/jni/org_coolreader_crengine_Engine.h index 559bc5a9c2..8c186d3b4e 100644 --- a/android/jni/org_coolreader_crengine_Engine.h +++ b/android/jni/org_coolreader_crengine_Engine.h @@ -25,7 +25,7 @@ extern "C" { * Signature: ([Ljava/lang/String;I)Z */ JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_initInternal - (JNIEnv *, jclass, jobjectArray, jint sdk_int); + (JNIEnv *, jclass, jobjectArray, jint); /* * Class: org_coolreader_crengine_Engine @@ -123,6 +123,14 @@ JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_haveFcLangCodeInt JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_checkFontLanguageCompatibilityInternal (JNIEnv *, jclass, jstring, jstring); +/* + * Class: org_coolreader_crengine_Engine + * Method: listFilesInternal + * Signature: (Ljava/io/File;)[Ljava/io/File; + */ +JNIEXPORT jobjectArray JNICALL Java_org_coolreader_crengine_Engine_listFilesInternal + (JNIEnv *, jclass, jobject); + /* * Class: org_coolreader_crengine_Engine * Method: isLink diff --git a/android/src/org/coolreader/crengine/Engine.java b/android/src/org/coolreader/crengine/Engine.java index 9786a87fc9..9981dd42ec 100644 --- a/android/src/org/coolreader/crengine/Engine.java +++ b/android/src/org/coolreader/crengine/Engine.java @@ -640,6 +640,8 @@ public void initAgain() { */ private native static boolean checkFontLanguageCompatibilityInternal(String fontFace, String langCode); + private native static File[] listFilesInternal(File dir); + public static void suspendLongOperation() { suspendLongOperationInternal(); } @@ -648,6 +650,10 @@ public synchronized static boolean checkFontLanguageCompatibility(String fontFac return checkFontLanguageCompatibilityInternal(fontFace, langCode); } + public static synchronized File[] listFiles(File dir) { + return listFilesInternal(dir); + } + /** * Finds the corresponding language code in embedded FontConfig language orthography catalog. * diff --git a/android/src/org/coolreader/crengine/Scanner.java b/android/src/org/coolreader/crengine/Scanner.java index b99b2b8c59..e3c2147a07 100644 --- a/android/src/org/coolreader/crengine/Scanner.java +++ b/android/src/org/coolreader/crengine/Scanner.java @@ -111,7 +111,15 @@ public boolean listDirectory(FileInfo baseDir) } try { File dir = new File(baseDir.pathname); - File[] items = dir.listFiles(); + //File[] items = dir.listFiles(); + // To resolve unhandled exception + // 'JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal continuation byte 0' + // that can be produced by invalid filename (broken sdcard, etc) + // or 'JNI WARNING: input is not valid Modified UTF-8: illegal start byte 0xf0' + // that can be generated if 4-byte UTF-8 sequence found in the filename, + // we implement own directory listing method instead of File.listFiles(). + // TODO: replace other occurrences of the method File.listFiles(). + File[] items = Engine.listFiles(dir); // process normal files if ( items!=null ) { for ( File f : items ) { From 65719eacfec80fea7cc89c2f159d8c5ff90363a2 Mon Sep 17 00:00:00 2001 From: Aleksey Chernov Date: Wed, 25 Dec 2019 09:11:39 +0400 Subject: [PATCH 8/8] Refactoring in Java_org_coolreader_crengine_Engine_listFilesInternal(), also added safety checks. --- android/jni/cr3engine.cpp | 41 +++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/android/jni/cr3engine.cpp b/android/jni/cr3engine.cpp index 62358aa5c3..d71eef67d5 100644 --- a/android/jni/cr3engine.cpp +++ b/android/jni/cr3engine.cpp @@ -717,34 +717,41 @@ JNIEXPORT jboolean JNICALL Java_org_coolreader_crengine_Engine_checkFontLanguage JNIEXPORT jobjectArray JNICALL Java_org_coolreader_crengine_Engine_listFilesInternal (JNIEnv *env, jclass, jobject jdir) { - jclass fileClass = env->FindClass("java/io/File"); - if (NULL == fileClass) + if (NULL == jdir) return NULL; - jmethodID mFileGetAbsolutePath = env->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;"); - if (NULL == mFileGetAbsolutePath) + jclass pjcFile = env->FindClass("java/io/File"); + if (NULL == pjcFile) return NULL; - jmethodID fileCtor = env->GetMethodID(fileClass, "", "(Ljava/lang/String;)V"); - if (NULL == fileCtor) + jmethodID pjmFile_GetAbsolutePath = env->GetMethodID(pjcFile, "getAbsolutePath", "()Ljava/lang/String;"); + if (NULL == pjmFile_GetAbsolutePath) return NULL; - jstring jpathname = (jstring)env->CallObjectMethod(jdir, mFileGetAbsolutePath); + jmethodID pjmFile_Ctor = env->GetMethodID(pjcFile, "", "(Ljava/lang/String;)V"); + if (NULL == pjmFile_Ctor) + return NULL; + jstring jpathname = (jstring)env->CallObjectMethod(jdir, pjmFile_GetAbsolutePath); jboolean iscopy; const char * s = env->GetStringUTFChars(jpathname, &iscopy); lString16 path = (CRJNIEnv::sdk_int >= ANDROID_SDK_M) ? Utf8ToUnicode(s) : Wtf8ToUnicode(s); - LVContainerRef dir = LVOpenDirectory(path); + env->ReleaseStringUTFChars(jpathname, s); jobjectArray jarray = NULL; + LVContainerRef dir = LVOpenDirectory(path); if ( !dir.isNull() ) { jstring emptyString = env->NewStringUTF(""); - jobject emptyFile = env->NewObject(fileClass, fileCtor, emptyString); - jarray = env->NewObjectArray(dir->GetObjectCount(), fileClass, emptyFile); - for (int i = 0; i < dir->GetObjectCount(); i++) { - const LVContainerItemInfo *item = dir->GetObjectInfo(i); - lString16 fileName = path + "/" + item->GetName(); - jstring jfilename = env->NewStringUTF((CRJNIEnv::sdk_int >= ANDROID_SDK_M) ? UnicodeToUtf8(fileName).c_str() : UnicodeToWtf8(fileName).c_str()); - jobject jfile = env->NewObject(fileClass, fileCtor, jfilename); - env->SetObjectArrayElement(jarray, i, jfile); + jobject emptyFile = env->NewObject(pjcFile, pjmFile_Ctor, emptyString); + jarray = env->NewObjectArray(dir->GetObjectCount(), pjcFile, emptyFile); + if (NULL != jarray) { + for (int i = 0; i < dir->GetObjectCount(); i++) { + const LVContainerItemInfo *item = dir->GetObjectInfo(i); + lString16 fileName = path + "/" + item->GetName(); + jstring jfilename = env->NewStringUTF( + (CRJNIEnv::sdk_int >= ANDROID_SDK_M) ? UnicodeToUtf8(fileName).c_str() + : UnicodeToWtf8(fileName).c_str()); + jobject jfile = env->NewObject(pjcFile, pjmFile_Ctor, jfilename); + if (NULL != jfile) + env->SetObjectArrayElement(jarray, i, jfile); + } } } - env->ReleaseStringUTFChars(jpathname, s); return jarray; }