I'm using a modified version of this code (Update: that answer has since been updated to use correct code, but this question still carries value since it contains relevant test cases and discussions for this problem) to store a single object after stringification in chunked keys inside of sync storage.
Note that sync storage has a maximum quota size per item. So, I have those maxLengthPerItem
and maxValueLength
variables.
function lengthInUtf8Bytes(str) {
// by: https://stackoverflow.com/a/5515960/2675672
// Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence.
var m = encodeURIComponent(str).match(/%[89ABab]/g);
return str.length + (m ? m.length : 0);
}
function syncStore(key, objectToStore, callback) {
var jsonstr = JSON.stringify(objectToStore), i = 0, storageObj = {},
// (note: QUOTA_BYTES_PER_ITEM only on sync storage)
// subtract two for the quotes added by stringification
// extra -5 to err on the safe side
maxBytesPerItem = chrome.storage.sync.QUOTA_BYTES_PER_ITEM - NUMBER,
// since the key uses up some per-item quota, use
// "maxValueBytes" to see how much is left for the value
maxValueBytes, index, segment, counter;
console.log("jsonstr length is " + lengthInUtf8Bytes(jsonstr));
// split jsonstr into chunks and store them in an object indexed by `key_i`
while(jsonstr.length > 0) {
index = key + "_" + i++;
maxValueBytes = maxBytesPerItem - lengthInUtf8Bytes(index);
counter = maxValueBytes;
segment = jsonstr.substr(0, counter);
while(lengthInUtf8Bytes(segment) > maxValueBytes)
segment = jsonstr.substr(0, --counter);
storageObj[index] = segment;
jsonstr = jsonstr.substr(counter);
}
// later used by retriever function
storageObj[key] = i;
console.log((i + 1) + " keys used (= key + key_i)");
// say user saves till chunk 20 in case I
// in case II, user deletes several snippets and brings down
// total no. of "required" chunks to 15; however, the previous chunks
// (16-20) remain in memory unless they are "clear"ed.
chrome.storage.sync.clear(function(){
console.log(storageObj);
console.log(chrome.storage.sync);
chrome.storage.sync.set(storageObj, callback);
});
}
The problem is in this line:
maxLengthPerItem = chrome.storage.sync.QUOTA_BYTES_PER_ITEM - NUMBER,
The problem is that 5 is the minimum NUMBER
for which there's no error. Here's the sample code you can use to test my theory:
var len = 102000,
string = [...new Array(len)].map(x => 1).join(""),
Data = {
"my_text": string
},
key = "key";
syncStore(key, Data, function(){
console.log(chrome.runtime.lastError && chrome.runtime.lastError.message);
});
Using 4 yields MAX_QUOTA_BYTES_
PER_ITEM
exceed error. You can yourself adjust the value of len
(to 20000, 60000
< 102000
, etc.) to check my theory.
Question:
Why is the current method requiring exactly 5
as the minimum value? I know there's two quotes for stringification, but what about the other 3 characters? Where'd they come from?
Additionally, I've noticed that in textual Data
like this one,
even 5
does not work. In the specific case above, minimum NUMBER required is 6
.
Clarification:
The point of my question is not what are the other means to store data in sync.
The point of my question is why is the current method requiring exactly 5
(And why that textual data requires a 6.) Imho, my question is very specific and surely does not deserve a close vote.
Update: I've added new code which stores data based on measurement of length of UTF-8 bytes, but it still does not provide desirable results. I've also added code to more easily test my theory.