static BencArray *SerializeFileHistory(FileHistory& fileHistory, bool globalPrefsOnly) { BencArray *arr = new BencArray(); // Don't save more file entries than will be useful int minOpenCount = 0; if (globalPrefsOnly) { Vec<DisplayState *> frequencyList; fileHistory.GetFrequencyOrder(frequencyList); if (frequencyList.Count() > FILE_HISTORY_MAX_RECENT) minOpenCount = frequencyList.At(FILE_HISTORY_MAX_FREQUENT)->openCount / 2; } DisplayState *state; for (int index = 0; (state = fileHistory.Get(index)); index++) { // never forget pinned documents and documents we've remembered a password for bool forceSave = state->isPinned || state->decryptionKey != NULL; if (index >= MAX_REMEMBERED_FILES && !forceSave) continue; if (state->openCount < minOpenCount && index > FILE_HISTORY_MAX_RECENT && !forceSave) continue; BencDict *obj = DisplayState_Serialize(state, globalPrefsOnly); if (!obj) goto Error; arr->Add(obj); } return arr; Error: delete arr; return NULL; }
// for simplicity, favorites are serialized as an array. Element 2*i is // a name of the file, for favorite i, element 2*i+1 is an array of // page number integer/name string pairs static BencArray *SerializeFavorites(Favorites *favs) { BencArray *res = new BencArray(); for (size_t i = 0; i < favs->favs.Count(); i++) { FileFavs *fav = favs->favs.At(i); res->Add(fav->filePath); res->Add(SerializeFavData(fav)); } return res; }
static BencArray *SerializeFavData(FileFavs *fav) { BencArray *res = new BencArray(); for (size_t i = 0; i < fav->favNames.Count(); i++) { FavName *fn = fav->favNames.At(i); res->Add(fn->pageNo); res->Add(NullToEmpty(fn->name)); } return res; }
static BencDict *SerializeStruct(PrefInfo *info, size_t count, const void *structBase, BencDict *prefs=NULL, uint32_t bitmask=-1) { if (!prefs) prefs = new BencDict(); const char *base = (const char *)structBase; for (size_t i = 0; i < count; i++) { PrefInfo& meta = info[i]; if ((meta.bitfield & bitmask) != meta.bitfield) continue; switch (meta.type) { case Pref_Bool: prefs->Add(meta.name, (int64_t)*(bool *)(base + meta.offset)); break; case Pref_Int: prefs->Add(meta.name, (int64_t)*(int *)(base + meta.offset)); break; case Pref_Str: case Pref_UILang: if (*(const char **)(base + meta.offset)) prefs->AddRaw(meta.name, *(const char **)(base + meta.offset)); else delete prefs->Remove(meta.name); break; case Pref_WStr: if (*(const WCHAR **)(base + meta.offset)) prefs->Add(meta.name, *(const WCHAR **)(base + meta.offset)); else delete prefs->Remove(meta.name); break; case Pref_DisplayMode: prefs->Add(meta.name, DisplayModeConv::NameFromEnum(*(DisplayMode *)(base + meta.offset))); break; case Pref_Float: prefs->AddRaw(meta.name, ScopedMem<char>(str::Format("%.4f", *(float *)(base + meta.offset)))); break; case Pref_IntVec: Vec<int> *intVec = *(Vec<int> **)(base + meta.offset); if (intVec) { BencArray *array = new BencArray(); for (size_t idx = 0; idx < intVec->Count(); idx++) { array->Add(intVec->At(idx)); } prefs->Add(meta.name, array); } else delete prefs->Remove(meta.name); break; } } return prefs; }
static void DeserializeToc(BencDict *dict, DisplayState *ds) { BencArray *tocState = dict->GetArray(TOC_STATE_STR); if (!tocState) return; size_t len = tocState->Length(); ds->tocState = new Vec<int>(len); if (!ds->tocState) return; for (size_t i = 0; i < len; i++) { BencInt *intObj = tocState->GetInt(i); if (intObj) ds->tocState->Append((int)intObj->Value()); } }
static void BencTestArrayAppend() { BencArray *array = new BencArray(); for (size_t i = 1; i <= ITERATION_COUNT; i++) { array->Add(i); assert(array->Length() == i); } array->Add(new BencDict()); for (size_t i = 1; i <= ITERATION_COUNT; i++) { BencInt *obj = array->GetInt(i - 1); assert(obj && obj->Type() == BT_INT); assert(obj->Value() == i); assert(!array->GetString(i - 1)); assert(!array->GetArray(i - 1)); assert(!array->GetDict(i - 1)); } assert(!array->GetInt(ITERATION_COUNT)); assert(array->GetDict(ITERATION_COUNT)); BencTestRoundtrip(array); delete array; }
static void BencTestParseRawStrings() { BencArray array; array.AddRaw("a\x82"); array.AddRaw("a\x82", 1); BencString *raw = array.GetString(0); assert(raw && str::Eq(raw->RawValue(), "a\x82")); BencTestSerialization(raw, "2:a\x82"); raw = array.GetString(1); assert(raw && str::Eq(raw->RawValue(), "a")); BencTestSerialization(raw, "1:a"); BencDict dict; dict.AddRaw("1", "a\x82"); dict.AddRaw("2", "a\x82", 1); raw = dict.GetString("1"); assert(raw && str::Eq(raw->RawValue(), "a\x82")); BencTestSerialization(raw, "2:a\x82"); raw = dict.GetString("2"); assert(raw && str::Eq(raw->RawValue(), "a")); BencTestSerialization(raw, "1:a"); }
BencArray *BencArray::Decode(const char *bytes, size_t *lenOut) { if (!bytes || *bytes != 'l') return NULL; BencArray *list = new BencArray(); size_t ix = 1; while (bytes[ix] != 'e') { size_t len; BencObj *obj = BencObj::Decode(bytes + ix, &len); if (!obj) { delete list; return NULL; } ix += len; list->Add(obj); } if (lenOut) *lenOut = ix + 1; return list; }
static void BencTestStress() { char key[64]; char val[64]; WCHAR tval[64]; Vec<BencObj*> stack(29); BencDict *startDict = new BencDict(); BencDict *d = startDict; BencArray *a = NULL; srand((unsigned int)time(NULL)); // generate new dict or array with 5% probability each, close an array or // dict with 8% probability (less than 10% probability of opening one, to // encourage nesting), generate int, string or raw strings uniformly // across the remaining 72% probability for (int i = 0; i < 10000; i++) { int n = rand() % 100; if (n < 5) { BencDict *nd = new BencDict(); if (a) { a->Add(nd); } else { GenRandStr(key, dimof(key)); d->Add(key, nd); } stack.Push(nd); d = nd; a = NULL; } else if (n < 10) { BencArray *na = new BencArray(); if (a) { a->Add(na); } else { GenRandStr(key, dimof(key)); d->Add(key, na); } stack.Push(na); d = NULL; a = na; } else if (n < 18) { if (stack.Count() > 0) { n = rand() % 100; stack.Pop(); BencObj *o = startDict; if (stack.Count() > 0) { o = stack.Last(); } a = NULL; d = NULL; if (BT_ARRAY == o->Type()) { a = static_cast<BencArray *>(o); } else { d = static_cast<BencDict *>(o); } } } else if (n < (18 + 24)) { int64_t v = rand(); if (a) { a->Add(v); } else { GenRandStr(key, dimof(key)); d->Add(key, v); } } else if (n < (18 + 24 + 24)) { GenRandStr(val, dimof(val)); if (a) { a->AddRaw((const char*)val); } else { GenRandStr(key, dimof(key)); d->AddRaw((const char*)key, val); } } else { GenRandTStr(tval, dimof(tval)); if (a) { a->Add(tval); } else { GenRandStr(key, dimof(key)); d->Add((const char*)key, (const WCHAR *)val); } } } char *s = startDict->Encode(); free(s); delete startDict; }
static void DeserializePrefs(const char *prefsTxt, SerializableGlobalPrefs& globalPrefs, FileHistory& fh, Favorites *favs) { BencObj *obj = BencObj::Decode(prefsTxt); if (!obj || obj->Type() != BT_DICT) goto Exit; BencDict *prefs = static_cast<BencDict *>(obj); BencDict *global = prefs->GetDict(GLOBAL_PREFS_STR); if (!global) goto Exit; Retrieve(global, TOOLBAR_VISIBLE_STR, globalPrefs.toolbarVisible); Retrieve(global, TOC_VISIBLE_STR, globalPrefs.tocVisible); Retrieve(global, FAV_VISIBLE_STR, globalPrefs.favVisible); Retrieve(global, SIDEBAR_DX_STR, globalPrefs.sidebarDx); Retrieve(global, TOC_DY_STR, globalPrefs.tocDy); Retrieve(global, PDF_ASSOCIATE_DONT_ASK_STR, globalPrefs.pdfAssociateDontAskAgain); Retrieve(global, PDF_ASSOCIATE_ASSOCIATE_STR, globalPrefs.pdfAssociateShouldAssociate); Retrieve(global, ESC_TO_EXIT_STR, globalPrefs.escToExit); Retrieve(global, USE_SYS_COLORS_STR, globalPrefs.useSysColors); Retrieve(global, BG_COLOR_STR, globalPrefs.bgColor); Retrieve(global, ENABLE_AUTO_UPDATE_STR, globalPrefs.enableAutoUpdate); Retrieve(global, REMEMBER_OPENED_FILES_STR, globalPrefs.rememberOpenedFiles); Retrieve(global, GLOBAL_PREFS_ONLY_STR, globalPrefs.globalPrefsOnly); Retrieve(global, SHOW_RECENT_FILES_STR, globalPrefs.showStartPage); Retrieve(global, DISPLAY_MODE_STR, globalPrefs.defaultDisplayMode); Retrieve(global, ZOOM_VIRTUAL_STR, globalPrefs.defaultZoom); Retrieve(global, WINDOW_STATE_STR, globalPrefs.windowState); Retrieve(global, WINDOW_X_STR, globalPrefs.windowPos.x); Retrieve(global, WINDOW_Y_STR, globalPrefs.windowPos.y); Retrieve(global, WINDOW_DX_STR, globalPrefs.windowPos.dx); Retrieve(global, WINDOW_DY_STR, globalPrefs.windowPos.dy); Retrieve(global, INVERSE_SEARCH_COMMANDLINE, globalPrefs.inverseSearchCmdLine); Retrieve(global, ENABLE_TEX_ENHANCEMENTS_STR, globalPrefs.enableTeXEnhancements); Retrieve(global, VERSION_TO_SKIP_STR, globalPrefs.versionToSkip); RetrieveRaw(global, LAST_UPDATE_STR, globalPrefs.lastUpdateTime); const char *lang = GetRawString(global, UI_LANGUAGE_STR); const char *langCode = trans::ValidateLanguageCode(lang); if (langCode) globalPrefs.currentLanguage = langCode; Retrieve(global, FWDSEARCH_OFFSET, globalPrefs.fwdSearch.offset); Retrieve(global, FWDSEARCH_COLOR, globalPrefs.fwdSearch.color); Retrieve(global, FWDSEARCH_WIDTH, globalPrefs.fwdSearch.width); Retrieve(global, FWDSEARCH_PERMANENT, globalPrefs.fwdSearch.permanent); Retrieve(global, CBX_RIGHT2LEFT, globalPrefs.cbxR2L); Retrieve(global, OPEN_COUNT_WEEK_STR, globalPrefs.openCountWeek); int weekDiff = GetWeekCount() - globalPrefs.openCountWeek; globalPrefs.openCountWeek = GetWeekCount(); BencArray *fileHistory = prefs->GetArray(FILE_HISTORY_STR); if (!fileHistory) goto Exit; size_t dlen = fileHistory->Length(); for (size_t i = 0; i < dlen; i++) { BencDict *dict = fileHistory->GetDict(i); assert(dict); if (!dict) continue; DisplayState *state = DeserializeDisplayState(dict, globalPrefs.globalPrefsOnly); if (state) { // "age" openCount statistics (cut in in half after every week) state->openCount >>= weekDiff; fh.Append(state); } } BencArray *favsArr = prefs->GetArray(FAVS_STR); if (!favsArr) goto Exit; for (size_t i = 0; i < favsArr->Length(); i += 2) { BencString *filePathBenc = favsArr->GetString(i); BencArray *favData = favsArr->GetArray(i+1); if (!filePathBenc || !favData) break; ScopedMem<WCHAR> filePath(filePathBenc->Value()); for (size_t j = 0; j < favData->Length(); j += 2) { // we're lenient about errors BencInt *pageNoBenc = favData->GetInt(j); BencString *nameBenc = favData->GetString(j + 1); if (!pageNoBenc || !nameBenc) break; int pageNo = (int)pageNoBenc->Value(); ScopedMem<WCHAR> name(nameBenc->Value()); favs->AddOrReplace(filePath, pageNo, EmptyToNull(name)); } } Exit: delete obj; }
static BencDict *DisplayState_Serialize(DisplayState *ds, bool globalPrefsOnly) { BencDict *prefs = new BencDict(); if (!prefs) return NULL; prefs->Add(FILE_STR, ds->filePath); if (ds->decryptionKey) prefs->AddRaw(DECRYPTION_KEY_STR, ds->decryptionKey); prefs->Add(OPEN_COUNT_STR, ds->openCount); prefs->Add(IS_PINNED_STR, ds->isPinned); if (globalPrefsOnly || ds->useGlobalValues) { prefs->Add(USE_GLOBAL_VALUES_STR, TRUE); return prefs; } const WCHAR *mode = DisplayModeConv::NameFromEnum(ds->displayMode); prefs->Add(DISPLAY_MODE_STR, mode); prefs->Add(PAGE_NO_STR, ds->pageNo); prefs->Add(REPARSE_IDX_STR, ds->reparseIdx); prefs->Add(ROTATION_STR, ds->rotation); prefs->Add(SCROLL_X_STR, ds->scrollPos.x); prefs->Add(SCROLL_Y_STR, ds->scrollPos.y); prefs->Add(WINDOW_STATE_STR, ds->windowState); prefs->Add(WINDOW_X_STR, ds->windowPos.x); prefs->Add(WINDOW_Y_STR, ds->windowPos.y); prefs->Add(WINDOW_DX_STR, ds->windowPos.dx); prefs->Add(WINDOW_DY_STR, ds->windowPos.dy); prefs->Add(TOC_VISIBLE_STR, ds->tocVisible); prefs->Add(SIDEBAR_DX_STR, ds->sidebarDx); // BUG: 2140 if (!IsValidZoom(ds->zoomVirtual)) { CrashLogFmt("Invalid ds->zoomVirtual: %.4f\n", ds->zoomVirtual); const WCHAR *ext = str::FindCharLast(ds->filePath, L'.'); if (ext) { ScopedMem<char> extA(str::conv::ToUtf8(ext)); CrashLogFmt("File type: %s\n", extA.Get()); } CrashLogFmt("DisplayMode: %d\n", ds->displayMode); CrashLogFmt("PageNo: %d\n", ds->pageNo); } CrashIf(!IsValidZoom(ds->zoomVirtual)); ScopedMem<char> zoom(str::Format("%.4f", ds->zoomVirtual)); prefs->AddRaw(ZOOM_VIRTUAL_STR, zoom); if (ds->tocState && ds->tocState->Count() > 0) { BencArray *tocState = new BencArray(); if (tocState) { for (size_t i = 0; i < ds->tocState->Count(); i++) { tocState->Add(ds->tocState->At(i)); } prefs->Add(TOC_STATE_STR, tocState); } } return prefs; }
static void DeserializeStruct(PrefInfo *info, size_t count, void *structBase, BencDict *prefs) { char *base = (char *)structBase; BencInt *intObj; BencString *strObj; BencArray *arrObj; for (size_t i = 0; i < count; i++) { PrefInfo& meta = info[i]; switch (meta.type) { case Pref_Bool: if ((intObj = prefs->GetInt(meta.name))) *(bool *)(base + meta.offset) = intObj->Value() != 0; break; case Pref_Int: if ((intObj = prefs->GetInt(meta.name))) *(int *)(base + meta.offset) = (int)intObj->Value(); break; case Pref_Str: if ((strObj = prefs->GetString(meta.name))) str::ReplacePtr((char **)(base + meta.offset), strObj->RawValue()); break; case Pref_WStr: if ((strObj = prefs->GetString(meta.name))) { ScopedMem<WCHAR> str(strObj->Value()); if (str) str::ReplacePtr((WCHAR **)(base + meta.offset), str); } break; case Pref_DisplayMode: if ((strObj = prefs->GetString(meta.name))) { ScopedMem<WCHAR> mode(strObj->Value()); if (mode) DisplayModeConv::EnumFromName(mode, (DisplayMode *)(base + meta.offset)); } break; case Pref_Float: if ((strObj = prefs->GetString(meta.name))) { // note: this might round the value for files produced with versions // prior to 1.6 and on a system where the decimal mark isn't a '.' // (the difference should be hardly notable, though) *(float *)(base + meta.offset) = (float)atof(strObj->RawValue()); } break; case Pref_IntVec: if ((arrObj = prefs->GetArray(meta.name))) { size_t len = arrObj->Length(); Vec<int> *intVec = new Vec<int>(len); if (intVec) { for (size_t idx = 0; idx < len; idx++) { if ((intObj = arrObj->GetInt(idx))) intVec->Append((int)intObj->Value()); } delete *(Vec<int> **)(base + meta.offset); *(Vec<int> **)(base + meta.offset) = intVec; } } break; case Pref_UILang: if ((strObj = prefs->GetString(meta.name))) { // ensure language code is valid const char *langCode = trans::ValidateLangCode(strObj->RawValue()); if (langCode) *(const char **)(base + meta.offset) = langCode; } break; } } }
static void DeserializePrefs(const char *prefsTxt, SerializableGlobalPrefs& globalPrefs, FileHistory& fh, Favorites *favs) { BencObj *obj = BencObj::Decode(prefsTxt); if (!obj || obj->Type() != BT_DICT) goto Exit; BencDict *prefs = static_cast<BencDict *>(obj); BencDict *global = prefs->GetDict(GLOBAL_PREFS_STR); if (!global) goto Exit; DeserializeStruct(gGlobalPrefInfo, dimof(gGlobalPrefInfo), &globalPrefs, global); free(globalPrefs.prevSerialization); globalPrefs.prevSerialization = global->Encode(); int weekDiff = GetWeekCount() - globalPrefs.openCountWeek; globalPrefs.openCountWeek = GetWeekCount(); BencArray *fileHistory = prefs->GetArray(FILE_HISTORY_STR); if (!fileHistory) goto Exit; size_t dlen = fileHistory->Length(); for (size_t i = 0; i < dlen; i++) { BencDict *dict = fileHistory->GetDict(i); assert(dict); if (!dict) continue; DisplayState *state = DeserializeDisplayState(dict, globalPrefs.globalPrefsOnly); if (state) { // "age" openCount statistics (cut in in half after every week) state->openCount >>= weekDiff; fh.Append(state); } } BencArray *favsArr = prefs->GetArray(FAVS_STR); if (!favsArr) goto Exit; for (size_t i = 0; i < favsArr->Length(); i += 2) { BencString *filePathBenc = favsArr->GetString(i); BencArray *favData = favsArr->GetArray(i+1); if (!filePathBenc || !favData) break; ScopedMem<WCHAR> filePath(filePathBenc->Value()); for (size_t j = 0; j < favData->Length(); j += 2) { // we're lenient about errors BencInt *pageNoBenc = favData->GetInt(j); BencString *nameBenc = favData->GetString(j + 1); if (!pageNoBenc || !nameBenc) break; int pageNo = (int)pageNoBenc->Value(); ScopedMem<WCHAR> name(nameBenc->Value()); favs->AddOrReplace(filePath, pageNo, EmptyToNull(name)); } } Exit: delete obj; }