jens@0
|
1 |
/*
|
jens@0
|
2 |
* Chunk.cpp
|
jens@0
|
3 |
* Ottoman
|
jens@0
|
4 |
*
|
jens@0
|
5 |
* Created by Jens Alfke on 8/27/09.
|
jens@0
|
6 |
* Copyright 2009 Jens Alfke. All rights reserved.
|
jens@0
|
7 |
* BSD-Licensed: See the file "LICENSE.txt" for details.
|
jens@0
|
8 |
*/
|
jens@0
|
9 |
|
jens@0
|
10 |
#include "Chunk.h"
|
jens@0
|
11 |
#include <assert.h>
|
jens@0
|
12 |
#include <errno.h>
|
jens@0
|
13 |
#include <stddef.h>
|
jens@0
|
14 |
|
jens@0
|
15 |
namespace Mooseyard {
|
jens@0
|
16 |
|
jens@0
|
17 |
uint16_t Chunk::type() const {
|
jens@0
|
18 |
return _keyLength == 0xFFFF ?(uint16_t)_type :KeyValueChunk::kChunkType;
|
jens@0
|
19 |
}
|
jens@0
|
20 |
|
jens@0
|
21 |
const KeyValueChunk* Chunk::asKeyValue() const {
|
jens@0
|
22 |
return _keyLength == 0xFFFF ?NULL :(const KeyValueChunk*)this;
|
jens@0
|
23 |
}
|
jens@0
|
24 |
|
jens@0
|
25 |
size_t Chunk::writeMultiple (File *file, uint16_t type,
|
jens@0
|
26 |
Blob blobs[], int count) throw(File::Error) {
|
jens@0
|
27 |
assert(type != KeyValueChunk::kChunkType);
|
jens@0
|
28 |
Blob nuBlobs[count+1];
|
jens@0
|
29 |
uint32_t size = sizeof(Chunk);
|
jens@0
|
30 |
for (int i=0; i<count; i++) {
|
jens@0
|
31 |
nuBlobs[i+1] = blobs[i];
|
jens@0
|
32 |
size += blobs[i].length;
|
jens@0
|
33 |
}
|
jens@0
|
34 |
Chunk chunk(size, type);
|
jens@0
|
35 |
nuBlobs[0] = Blob(&chunk, sizeof(chunk));
|
jens@0
|
36 |
return file->writeMultiple(nuBlobs, count+1);
|
jens@0
|
37 |
}
|
jens@0
|
38 |
|
jens@0
|
39 |
size_t Chunk::writePadding (File *file) {
|
jens@2
|
40 |
off_t padding = file->position() & 0x03;
|
jens@0
|
41 |
if (padding == 0)
|
jens@0
|
42 |
return 0;
|
jens@0
|
43 |
else {
|
jens@0
|
44 |
padding = 4 - padding;
|
jens@0
|
45 |
uint32_t zero = 0;
|
jens@2
|
46 |
Blob pad(&zero, (size_t)padding);
|
jens@0
|
47 |
return writeMultiple(file, kChunkTypePadding, &pad, 1);
|
jens@0
|
48 |
}
|
jens@0
|
49 |
}
|
jens@0
|
50 |
|
jens@0
|
51 |
|
jens@0
|
52 |
|
jens@0
|
53 |
void KeyValueChunk::validate() const throw(File::Error) {
|
jens@0
|
54 |
if (_keyLength >= 0xFFFFU)
|
jens@0
|
55 |
throw File::Error(ERANGE, "Invalid key length (0xFFFF)");
|
jens@0
|
56 |
if (valuePtr() > end())
|
jens@0
|
57 |
throw File::Error(ERANGE, "Bad KeyValueChunk (key-length too large to fit)");
|
jens@0
|
58 |
}
|
jens@0
|
59 |
|
jens@0
|
60 |
Blob KeyValueChunk::key() const {
|
jens@0
|
61 |
return Blob(&_keyLength +1, _keyLength); // KeyValueChunk doesn't have _type
|
jens@0
|
62 |
}
|
jens@0
|
63 |
|
jens@0
|
64 |
const char* KeyValueChunk::valuePtr() const {
|
jens@0
|
65 |
size_t vptr = (size_t) key().end();
|
jens@0
|
66 |
vptr = (vptr+3) & ~3; // 4-byte align
|
jens@0
|
67 |
return (const char*)vptr;
|
jens@0
|
68 |
}
|
jens@0
|
69 |
|
jens@0
|
70 |
Blob KeyValueChunk::value() const {
|
jens@0
|
71 |
const char *vp = valuePtr();
|
jens@0
|
72 |
return Blob(vp, (const char*)end()-vp);
|
jens@0
|
73 |
}
|
jens@0
|
74 |
|
jens@0
|
75 |
bool KeyValueChunk::hasKey (Blob key) const {
|
jens@0
|
76 |
return key.length==_keyLength
|
jens@0
|
77 |
&& memcmp(key.bytes, &_keyLength +1, key.length)==0;
|
jens@0
|
78 |
}
|
jens@0
|
79 |
|
jens@0
|
80 |
size_t KeyValueChunk::write (File* file, FilePosition pos, Blob key, Blob value) throw(File::Error) {
|
jens@0
|
81 |
if (key.length >= 0xFFFFU)
|
jens@0
|
82 |
throw File::Error(ERANGE, "Key too long (>=64k)");
|
jens@0
|
83 |
uint16_t keyLength = key.length;
|
jens@0
|
84 |
uint32_t size = sizeof(uint32_t) + sizeof(uint16_t) + keyLength;
|
jens@0
|
85 |
size_t pad = (pos + size) & 0x3;
|
jens@0
|
86 |
if (pad)
|
jens@0
|
87 |
pad = 4-pad;
|
jens@0
|
88 |
uint32_t zeros = 0;
|
jens@0
|
89 |
size += pad + value.length;
|
jens@0
|
90 |
|
jens@0
|
91 |
Chunk chunk(size, KeyValueChunk::kChunkType);
|
jens@0
|
92 |
chunk._keyLength = keyLength;
|
jens@0
|
93 |
Blob b[4] = {
|
jens@0
|
94 |
Blob(&chunk, sizeof(chunk._size)+sizeof(chunk._keyLength)),
|
jens@0
|
95 |
key,
|
jens@0
|
96 |
Blob(&zeros, pad),
|
jens@0
|
97 |
value};
|
jens@0
|
98 |
return file->writeMultiple(b,4);
|
jens@0
|
99 |
}
|
jens@0
|
100 |
|
jens@0
|
101 |
|
jens@0
|
102 |
|
jens@0
|
103 |
ChunkIterator::ChunkIterator (File* file, FilePosition start)
|
jens@0
|
104 |
:_file(file),
|
jens@0
|
105 |
_pos(start),
|
jens@2
|
106 |
_length((FilePosition)_file->length()),
|
jens@0
|
107 |
_chunk(NULL)
|
jens@0
|
108 |
{
|
jens@0
|
109 |
_loadChunk();
|
jens@0
|
110 |
}
|
jens@0
|
111 |
|
jens@0
|
112 |
void ChunkIterator::_loadChunk() {
|
jens@0
|
113 |
if (_pos < _length) {
|
jens@0
|
114 |
_chunk = (const Chunk*) _file->mappedPosition(_pos);
|
jens@0
|
115 |
if (_chunk->size() < sizeof(Chunk) || _pos+_chunk->size() > _length)
|
jens@0
|
116 |
_chunk = NULL;
|
jens@0
|
117 |
} else {
|
jens@0
|
118 |
_chunk = NULL;
|
jens@0
|
119 |
}
|
jens@0
|
120 |
|
jens@0
|
121 |
}
|
jens@0
|
122 |
|
jens@0
|
123 |
bool ChunkIterator::atEOF() const {
|
jens@0
|
124 |
return _pos == _length;
|
jens@0
|
125 |
}
|
jens@0
|
126 |
|
jens@0
|
127 |
bool ChunkIterator::next() {
|
jens@0
|
128 |
if (_chunk) {
|
jens@0
|
129 |
_pos += _chunk->size();
|
jens@0
|
130 |
_loadChunk();
|
jens@0
|
131 |
}
|
jens@0
|
132 |
return _chunk != NULL;
|
jens@0
|
133 |
}
|
jens@0
|
134 |
|
jens@0
|
135 |
}
|