From f451b66f19ee669efe37d885e6b711f2dec17a46 Mon Sep 17 00:00:00 2001 From: Nikolay Pultsin Date: Sun, 6 Sep 2009 12:08:06 +0000 Subject: [PATCH] memory optimization: hyperlink information is moved to the cache files instead of memory git-svn-id: https://only.mawhrin.net/repos/FBReaderJ/trunk@1013 6a642e6f-84f6-412e-ac94-c4a38d5a04b0 --- .../fbreader/bookmodel/BookModel.java | 69 ++++++++++++++++--- .../fbreader/fbreader/FBReader.java | 6 +- .../fbreader/formats/oeb/OEBBookReader.java | 32 ++++++++- .../fbreader/formats/xhtml/XHTMLReader.java | 23 +++++-- .../xhtml/XHTMLTagHyperlinkAction.java | 13 +++- .../core/application/ZLApplication.java | 4 -- .../text/model/CachedCharStorage.java | 4 +- .../zlibrary/text/model/CharStorage.java | 2 +- 8 files changed, 121 insertions(+), 32 deletions(-) diff --git a/src/org/geometerplus/fbreader/bookmodel/BookModel.java b/src/org/geometerplus/fbreader/bookmodel/BookModel.java index 5e76515f6..a49a59d6e 100644 --- a/src/org/geometerplus/fbreader/bookmodel/BookModel.java +++ b/src/org/geometerplus/fbreader/bookmodel/BookModel.java @@ -30,6 +30,8 @@ import org.geometerplus.fbreader.library.Book; import org.geometerplus.fbreader.formats.*; public final class BookModel { + private static String CacheDirectory = "/sdcard/Books/.FBReader"; + public static BookModel createModel(Book book) { FormatPlugin plugin = PluginCollection.instance().getPlugin(book.File); if (plugin == null) { @@ -49,18 +51,17 @@ public final class BookModel { private final ZLImageMap myImageMap = new ZLImageMap(); public final Book Book; - public final ZLTextModel BookTextModel = new ZLTextWritablePlainModel(null, 1024, 65536, "/sdcard/Books/.FBReader", "cache", myImageMap); + public final ZLTextModel BookTextModel = new ZLTextWritablePlainModel(null, 1024, 65536, CacheDirectory, "cache", myImageMap); public final TOCTree TOCTree = new TOCTree(); private final HashMap myFootnotes = new HashMap(); - private final HashMap myInternalHyperlinks = new HashMap(); - public class Label { - public final ZLTextModel Model; + public static final class Label { + public final String ModelId; public final int ParagraphIndex; - Label(ZLTextModel model, int paragraphIndex) { - Model = model; + public Label(String modelId, int paragraphIndex) { + ModelId = modelId; ParagraphIndex = paragraphIndex; } } @@ -72,18 +73,68 @@ public final class BookModel { public ZLTextModel getFootnoteModel(String id) { ZLTextModel model = myFootnotes.get(id); if (model == null) { - model = new ZLTextWritablePlainModel(id, 8, 512, "/sdcard/Books/.FBReader", "cache" + myFootnotes.size(), myImageMap); + model = new ZLTextWritablePlainModel(id, 8, 512, CacheDirectory, "cache" + myFootnotes.size(), myImageMap); myFootnotes.put(id, model); } return model; } + private final CharStorage myInternalHyperlinks = new CachedCharStorage(32768, CacheDirectory, "links"); + private char[] myCurrentLinkBlock; + private int myCurrentLinkBlockOffset; + void addHyperlinkLabel(String label, ZLTextModel model, int paragraphNumber) { - myInternalHyperlinks.put(label, new Label(model, paragraphNumber)); + final String modelId = model.getId(); + final int labelLength = label.length(); + final int idLength = (modelId != null) ? modelId.length() : 0; + final int len = 4 + labelLength + idLength; + char[] block = myCurrentLinkBlock; + int offset = myCurrentLinkBlockOffset; + if ((block == null) || (offset + len > block.length)) { + if (block != null) { + myInternalHyperlinks.freezeLastBlock(); + } + block = myInternalHyperlinks.createNewBlock(len); + myCurrentLinkBlock = block; + offset = 0; + } + block[offset++] = (char)labelLength; + label.getChars(0, labelLength, block, offset); + offset += labelLength; + block[offset++] = (char)idLength; + if (idLength > 0) { + modelId.getChars(0, idLength, block, offset); + offset += idLength; + } + block[offset++] = (char)(paragraphNumber >> 16); + block[offset++] = (char)paragraphNumber; + myCurrentLinkBlockOffset = offset; } public Label getLabel(String id) { - return myInternalHyperlinks.get(id); + System.err.println("searching label for " + id); + final int len = id.length(); + final int size = myInternalHyperlinks.size(); + for (int i = 0; i < size; ++i) { + final char[] block = myInternalHyperlinks.block(i); + for (int offset = 0; offset < block.length; ) { + final int labelLength = (int)block[offset++]; + if (labelLength == 0) { + break; + } + final int idLength = (int)block[offset + labelLength]; + if ((labelLength != len) || !id.equals(new String(block, offset, labelLength))) { + offset += labelLength + idLength + 3; + continue; + } + offset += labelLength + 1; + final String modelId = (idLength > 0) ? new String(block, offset, idLength) : null; + offset += idLength; + final int paragraphNumber = (((int)block[offset++]) << 16) + (int)block[offset]; + return new Label(modelId, paragraphNumber); + } + } + return null; } void addImage(String id, ZLImage image) { diff --git a/src/org/geometerplus/fbreader/fbreader/FBReader.java b/src/org/geometerplus/fbreader/fbreader/FBReader.java index 10d2e1e1d..e16dbf115 100644 --- a/src/org/geometerplus/fbreader/fbreader/FBReader.java +++ b/src/org/geometerplus/fbreader/fbreader/FBReader.java @@ -190,11 +190,11 @@ public final class FBReader extends ZLApplication { void tryOpenFootnote(String id) { if (Model != null) { BookModel.Label label = Model.getLabel(id); - if ((label != null) && (label.Model != null)) { - if (label.Model == Model.BookTextModel) { + if (label != null) { + if (label.ModelId == null) { BookTextView.gotoPosition(label.ParagraphIndex, 0, 0); } else { - FootnoteView.setModel(label.Model); + FootnoteView.setModel(Model.getFootnoteModel(label.ModelId)); setView(FootnoteView); FootnoteView.gotoPosition(label.ParagraphIndex, 0, 0); } diff --git a/src/org/geometerplus/fbreader/formats/oeb/OEBBookReader.java b/src/org/geometerplus/fbreader/formats/oeb/OEBBookReader.java index 69f46b104..ae89f5693 100644 --- a/src/org/geometerplus/fbreader/formats/oeb/OEBBookReader.java +++ b/src/org/geometerplus/fbreader/formats/oeb/OEBBookReader.java @@ -57,6 +57,9 @@ class OEBBookReader extends ZLXMLReaderAdapter implements XMLNamespace { myModelReader = new BookReader(model); } + private TreeMap myFileNumbers = new TreeMap(); + private TreeMap myTOCLabels = new TreeMap(); + boolean readBook(ZLFile file) { myFilePrefix = MiscUtil.htmlDirectoryPrefix(file); @@ -75,7 +78,13 @@ class OEBBookReader extends ZLXMLReaderAdapter implements XMLNamespace { myModelReader.pushKind(FBTextKind.REGULAR); for (String name : myHtmlFileNames) { - new XHTMLReader(myModelReader).readFile(ZLFile.createFileByPath(myFilePrefix + name)); + final ZLFile xhtmlFile = ZLFile.createFileByPath(myFilePrefix + name); + final XHTMLReader reader = new XHTMLReader(myModelReader, myFileNumbers); + final String referenceName = reader.getFileAlias(MiscUtil.archiveEntryName(xhtmlFile.getPath())); + + myModelReader.addHyperlinkLabel(referenceName); + myTOCLabels.put(referenceName, myModelReader.Model.BookTextModel.getParagraphsNumber()); + reader.readFile(xhtmlFile, referenceName + '#'); myModelReader.insertEndOfSectionParagraph(); } @@ -84,6 +93,23 @@ class OEBBookReader extends ZLXMLReaderAdapter implements XMLNamespace { return true; } + private BookModel.Label getTOCLabel(String id) { + final int index = id.indexOf('#'); + final String path = (index >= 0) ? id.substring(0, index) : id; + Integer num = myFileNumbers.get(path); + if (num == null) { + return null; + } + if (index == -1) { + final Integer para = myTOCLabels.get(num.toString()); + if (para == null) { + return null; + } + return new BookModel.Label(null, para); + } + return myModelReader.Model.getLabel(num + id.substring(index)); + } + private void generateTOC() { if (myNCXTOCFileName != null) { final NCXReader ncxReader = new NCXReader(myModelReader); @@ -92,7 +118,7 @@ class OEBBookReader extends ZLXMLReaderAdapter implements XMLNamespace { if (!navigationMap.isEmpty()) { int level = 0; for (NCXReader.NavPoint point : navigationMap.values()) { - final BookModel.Label label = myModelReader.Model.getLabel(point.ContentHRef); + final BookModel.Label label = getTOCLabel(point.ContentHRef); int index = (label != null) ? label.ParagraphIndex : -1; while (level > point.Level) { myModelReader.endContentsParagraph(); @@ -115,7 +141,7 @@ class OEBBookReader extends ZLXMLReaderAdapter implements XMLNamespace { } for (Reference ref : myTourTOC.isEmpty() ? myGuideTOC : myTourTOC) { - final BookModel.Label label = myModelReader.Model.getLabel(ref.HRef); + final BookModel.Label label = getTOCLabel(ref.HRef); if (label != null) { final int index = label.ParagraphIndex; if (index != -1) { diff --git a/src/org/geometerplus/fbreader/formats/xhtml/XHTMLReader.java b/src/org/geometerplus/fbreader/formats/xhtml/XHTMLReader.java index 9dcb4e890..d79a62368 100644 --- a/src/org/geometerplus/fbreader/formats/xhtml/XHTMLReader.java +++ b/src/org/geometerplus/fbreader/formats/xhtml/XHTMLReader.java @@ -119,24 +119,33 @@ public class XHTMLReader extends ZLXMLReaderAdapter { private final BookReader myModelReader; String myPathPrefix; String myLocalPathPrefix; - String myReferenceName; + String myReferencePrefix; boolean myPreformatted; boolean myInsideBody; + private final Map myFileNumbers; - public XHTMLReader(BookReader modelReader) { + public XHTMLReader(BookReader modelReader, Map fileNumbers) { myModelReader = modelReader; + myFileNumbers = fileNumbers; } final BookReader getModelReader() { return myModelReader; } - public boolean readFile(ZLFile file) { + public final String getFileAlias(String fileName) { + Integer num = myFileNumbers.get(fileName); + if (num == null) { + num = myFileNumbers.size(); + myFileNumbers.put(fileName, num); + } + return num.toString(); + } + + public boolean readFile(ZLFile file, String referencePrefix) { fillTagTable(); - myReferenceName = MiscUtil.archiveEntryName(file.getPath()); - - myModelReader.addHyperlinkLabel(myReferenceName); + myReferencePrefix = referencePrefix; myPathPrefix = MiscUtil.htmlDirectoryPrefix(file); myLocalPathPrefix = MiscUtil.archiveEntryName(myPathPrefix); @@ -150,7 +159,7 @@ public class XHTMLReader extends ZLXMLReaderAdapter { public boolean startElementHandler(String tag, ZLStringMap attributes) { String id = attributes.getValue("id"); if (id != null) { - myModelReader.addHyperlinkLabel(myReferenceName + '#' + id); + myModelReader.addHyperlinkLabel(myReferencePrefix + id); } XHTMLTagAction action = (XHTMLTagAction)ourTagActions.get(tag.toLowerCase()); diff --git a/src/org/geometerplus/fbreader/formats/xhtml/XHTMLTagHyperlinkAction.java b/src/org/geometerplus/fbreader/formats/xhtml/XHTMLTagHyperlinkAction.java index dbf751776..fea22dd4d 100644 --- a/src/org/geometerplus/fbreader/formats/xhtml/XHTMLTagHyperlinkAction.java +++ b/src/org/geometerplus/fbreader/formats/xhtml/XHTMLTagHyperlinkAction.java @@ -43,13 +43,20 @@ class XHTMLTagHyperlinkAction extends XHTMLTagAction { myHyperlinkStack = ZLArrayUtils.createCopy(myHyperlinkStack, myHyperlinkStackSize, 2 * myHyperlinkStackSize); } if ((href != null) && (href.length() > 0)) { - String link = (href.charAt(0) == '#') ? (reader.myReferenceName + href) : href; + String link = href; final byte hyperlinkType; if (isReference(link)) { hyperlinkType = FBTextKind.EXTERNAL_HYPERLINK; } else { hyperlinkType = FBTextKind.INTERNAL_HYPERLINK; - link = reader.myLocalPathPrefix + link; + final int index = href.indexOf('#'); + if (index == 0) { + link = reader.myReferencePrefix + href.substring(1); + } else if (index > 0) { + link = reader.getFileAlias(reader.myLocalPathPrefix + href.substring(0, index)) + href.substring(index); + } else { + link = reader.getFileAlias(reader.myLocalPathPrefix + href); + } } myHyperlinkStack[myHyperlinkStackSize++] = hyperlinkType; modelReader.addHyperlinkControl(hyperlinkType, link); @@ -58,7 +65,7 @@ class XHTMLTagHyperlinkAction extends XHTMLTagAction { } final String name = xmlattributes.getValue("name"); if (name != null) { - modelReader.addHyperlinkLabel(reader.myReferenceName + '#' + name); + modelReader.addHyperlinkLabel(reader.myReferencePrefix + name); } } diff --git a/src/org/geometerplus/zlibrary/core/application/ZLApplication.java b/src/org/geometerplus/zlibrary/core/application/ZLApplication.java index 1e5583819..c81610b8a 100644 --- a/src/org/geometerplus/zlibrary/core/application/ZLApplication.java +++ b/src/org/geometerplus/zlibrary/core/application/ZLApplication.java @@ -79,10 +79,6 @@ public abstract class ZLApplication { protected ZLApplication() { ourInstance = this; - //if (ConfigAutoSavingOption.getValue()) { - //ZLOption.startAutoSave(ConfigAutoSaveTimeoutOption.getValue()); - //} - new MenubarCreator().read(ZLResourceFile.createResourceFile("data/default/menubar.xml")); } diff --git a/src/org/geometerplus/zlibrary/text/model/CachedCharStorage.java b/src/org/geometerplus/zlibrary/text/model/CachedCharStorage.java index 99af4392f..ab783c245 100644 --- a/src/org/geometerplus/zlibrary/text/model/CachedCharStorage.java +++ b/src/org/geometerplus/zlibrary/text/model/CachedCharStorage.java @@ -30,7 +30,7 @@ final class CachedCharStorageException extends RuntimeException { } } -final class CachedCharStorage implements CharStorage { +public final class CachedCharStorage implements CharStorage { private final int myBlockSize; private final ArrayList> myArray = new ArrayList>(); private final String myDirectoryName; @@ -40,7 +40,7 @@ final class CachedCharStorage implements CharStorage { return myDirectoryName + index + myFileExtension; } - CachedCharStorage(int blockSize, String directoryName, String fileExtension) { + public CachedCharStorage(int blockSize, String directoryName, String fileExtension) { myBlockSize = blockSize; myDirectoryName = directoryName + '/'; myFileExtension = '.' + fileExtension; diff --git a/src/org/geometerplus/zlibrary/text/model/CharStorage.java b/src/org/geometerplus/zlibrary/text/model/CharStorage.java index 4e744cf9b..bfb414cfb 100644 --- a/src/org/geometerplus/zlibrary/text/model/CharStorage.java +++ b/src/org/geometerplus/zlibrary/text/model/CharStorage.java @@ -19,7 +19,7 @@ package org.geometerplus.zlibrary.text.model; -interface CharStorage { +public interface CharStorage { int size(); char[] block(int index); char[] createNewBlock(int minimumLength);