I've been using localStorage to store some binary data in string format, and although the value is definitely set (alert
ing it immediately after setting, or even some time after setting, shows the correct value) it is lost when the page next loads.
At first I figured it might be because the data contained null bytes, so I redesigned the compressor so that it would never output them. However, this made no difference as the value is still lost.
I added localStorage.testing = 1
immediately after setting the binary data. This value is kept, even when the other is lost.
I am absolutely certain there is no code to delete localStorage.myitem
.
What could be causing this issue?
If it helps any, here is the data I'm trying to store, in hex:
0x1103c0a0 0xd6cf0305 0xc0a0d6cf 0x0307c0a0 0xd6cf0309 0xc0a0d6cf
0x030bc0a0 0xd6cf030d 0xc0a0d6cf 0x0311c0a0 0xd6cf0313 0xc0a0d6cf 0x0301
EDIT: I just tested with localStorage.testvalue = realvalue.replace(/[\x00-\x1f]/g,'');
and that successfully saved it. So, I'd like to know where the specification says that control characters may not be used in strings.
I've set up a test case, and ran the test in various browsers. The results are below (the inclusive ranges of character codes are mentioned). Tests started at the minimum browser version which support localStorage
.
- Chrome 5 - 20:
0x0000
- 0xFFFF
- Opera 10.50 - 12.00:
0x0000
- 0xFFFF
- Safari 4.0 - 5.1.7:
0x0000
- 0xFFFF
- Firefox 3.5 - 16alpha:
0x0000
- 0xD7FF
and 0xE000
- 0xFFFE
(0xD800-0xDFFF and 0xFFFF are turned in two characters after LS)
- IE8, IE9, IE10PP6:
0x0009
, 0x000A
, 0x000D
, 0x0020
- 0xD7FF
and 0xE000
- 0xFFFD
. (Other ranges are either ignored or cause an "Invalid argument" error).
0x0000
is a NULL-byte, which truncates all following characters in IE.
So, the character ranges 0x20
- 0xD7FF
and 0xE000
- 0xFFFD
plus 0x09
, 0x0A
and 0x0D
are safe.
I've created three test cases:
- The quickest test case, which creates a string with all characters, and tests the value after setting
localStorage
- A method which used the SPACE character (0x20) as a delimiter, to properly deal with browsers which create a character with length 2.
- The worst method, because IE throws an error for invalid strings. Each character is tested individually, which is quite expensive.
All test functions are available in JSFiddle, the first test case is visible below:
function run_test(lowerlimit, UPPERLIMIT) {
try {
if (!window.localStorage) {
// I recall that in one of the older Chrome version (4),
// localStorage === null
return 'Localstorage is not supported';
}
if (isNaN(lowerlimit) || isNaN(UPPERLIMIT) || lowerlimit > UPPERLIMIT) {
return 'One of the limits is not a valid number!';
}
var i = lowerlimit - 1;
var character_range = [];
while (++i < UPPERLIMIT) character_range.push(i);
input = String.fromCharCode.apply(String, character_range);
localStorage.setItem('chartest', input);
output = localStorage.getItem('chartest');
if (input === output) {
return true;
}
// Uh oh, not equal!
var result = [];
for (i=0; i<UPPERLIMIT-lowerlimit; i++) {
if (input[i] !== output[i]) {
result.push(i + lowerlimit);
}
}
return result;
}catch(e){return 'Error:' + e;}
}
After further testing, it appears that when localStorage says it stores strings, what it really means is that it stores string that match:
/^[\x00\x09\x0A\x0D\x20-\xff]*$/
So nothing before space is allowed, except HT, CR, LF and NUL.
That said, still no idea why.