1 /* $Id: memory_store.cpp 498292 2016-04-14 19:07:55Z ucko $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Vladimir Soussov
27 *
28 * File Description: RAM storage implementation
29 *
30 */
31
32 #include <ncbi_pch.hpp>
33 #include <dbapi/error_codes.hpp>
34 #include "memory_store.hpp"
35 #include <string.h>
36
37
38 #define NCBI_USE_ERRCODE_X Dbapi_DrvrMemStore
39
40
41 BEGIN_NCBI_SCOPE
42
43
x_AddBlock(void)44 CMemStore::SMemBlock* CMemStore::x_AddBlock(void)
45 {
46 SMemBlock* n_blk = new SMemBlock;
47
48 if ( !n_blk )
49 return 0;
50
51 n_blk->body = new char[m_BlockSize];
52 if ( !n_blk->body ) {
53 delete n_blk;
54 return 0;
55 }
56
57 n_blk->next = 0;
58 n_blk->free_space = m_BlockSize;
59 n_blk->prev = m_Last;
60
61 if (m_First) {
62 m_Last->next = n_blk;
63 } else {
64 m_First = m_Current = n_blk;
65 }
66
67 m_Last = n_blk;
68
69 return n_blk;
70 }
71
72
Append(const void * buff,size_t size)73 size_t CMemStore::Append(const void* buff, size_t size)
74 {
75 if (!buff || !size)
76 return 0;
77
78 if (!m_Last || !m_Last->free_space) {
79 if ( !x_AddBlock() )
80 return 0;
81 }
82
83 TSize f_free;
84 TSize n = 0;
85 char* b = (char*) buff;
86 if(size > kMax_BlobSize) size= kMax_BlobSize;
87 TSize nof_bytes = (TSize) size;
88
89 while (nof_bytes > 0) {
90 f_free = m_BlockSize - m_Last->free_space;
91
92 if (nof_bytes <= m_Last->free_space) {
93 // we have enough space in last block
94 memcpy(&m_Last->body[f_free], b+n, nof_bytes);
95 m_Last->free_space -= nof_bytes;
96 n += nof_bytes;
97 break;
98 }
99 // space in this block is insufficient
100
101 memcpy(&m_Last->body[f_free], b + n, m_Last->free_space);
102 n += m_Last->free_space;
103 nof_bytes -= m_Last->free_space;
104 m_Last->free_space = 0;
105 if ( !x_AddBlock() )
106 break;
107 }
108 m_Size += n;
109 return (size_t) n;
110 }
111
112
Read(void * buff,size_t size)113 size_t CMemStore::Read(void* buff, size_t size)
114 {
115
116 if (!m_Current || !buff || !size)
117 return 0;
118
119 if(size > kMax_BlobSize) size= kMax_BlobSize;
120
121 TSize n = 0;
122 char* b = (char*) buff;
123
124 for (TSize nof_bytes = (TSize) size; nof_bytes > 0; ) {
125 TSize f_free = m_BlockSize - m_Current->free_space;
126
127 if ((m_BlockPos + nof_bytes) <= f_free) {
128 // we have all needed bytes in this block
129 memcpy(b, &m_Current->body[m_BlockPos], nof_bytes);
130 m_BlockPos += nof_bytes;
131 n += nof_bytes;
132 if (m_BlockPos >= f_free) {
133 // we have read all bytes from this block
134 m_Current = m_Current->next;
135 m_BlockPos = 0;
136 }
137 break;
138 }
139 // we can read just a part from this block
140 TSize k = f_free - m_BlockPos;
141 memcpy(b, &m_Current->body[m_BlockPos], k);
142 n += k;
143 b += k;
144 nof_bytes -= k;
145 m_BlockPos = 0;
146
147 m_Current = m_Current->next;
148 if ( !m_Current )
149 break;
150 }
151
152 m_Pos += n;
153 return (size_t) n;
154 }
155
156
Peek(void * buff,size_t size) const157 size_t CMemStore::Peek(void* buff, size_t size) const
158 {
159 // Work with a partial shallow copy to avoid code duplication.
160 // (No need for m_First, m_Last, or m_Size.)
161 CMemStore ms2(m_BlockSize);
162 ms2.m_Current = m_Current;
163 ms2.m_Pos = m_Pos;
164 ms2.m_BlockPos = m_BlockPos;
165 return ms2.Read(buff, size);
166 }
167
168
PeekAt(void * buff,size_t start,size_t size) const169 size_t CMemStore::PeekAt(void* buff, size_t start, size_t size) const
170 {
171 // Work with a shallow copy to avoid code duplication.
172 CMemStore ms2(m_BlockSize);
173 ms2.m_First = m_First;
174 ms2.m_Current = m_Current;
175 ms2.m_Pos = m_Pos;
176 ms2.m_BlockPos = m_BlockPos;
177 ms2.m_Size = m_Size;
178 // Set m_Last for the sake of Seek, but make sure it winds up NULL
179 // to avoid premature cleanup.
180 try {
181 ms2.m_Last = m_Last;
182 ms2.Seek(start, eHead);
183 } catch (...) {
184 ms2.m_Last = NULL;
185 throw;
186 }
187 ms2.m_Last = NULL;
188 return ms2.Read(buff, size);
189 }
190
191
x_SeekCURR(CMemStore::TSize offset)192 CMemStore::TSize CMemStore::x_SeekCURR(CMemStore::TSize offset)
193 {
194 if ( !m_Current )
195 return x_SeekTAIL(offset);
196
197 if (offset == 0)
198 return m_Pos;
199
200 if (offset <= -m_Pos)
201 return x_SeekHEAD(0);
202
203 if (offset > 0) {
204 // go toward the tail
205 while (offset > 0) {
206 TSize n = m_BlockSize - m_Current->free_space;
207
208 if ((m_BlockPos + offset) < n) {
209 // we have to move inside this block
210 m_BlockPos += offset;
211 m_Pos += offset;
212 break;
213 }
214 // we have to move outside the block
215 n -= m_BlockPos;
216 m_Pos += n;
217 m_BlockPos = 0;
218 m_Current = m_Current->next;
219 if (!m_Current)
220 break;
221 offset -= n;
222 }
223 }
224 else {
225 // go toward the head
226 while (offset < 0) {
227 if ((m_BlockPos + offset) >= 0) {
228 // we have to move inside this block
229 m_BlockPos += offset;
230 m_Pos += offset;
231 break;
232 }
233 // we have to move outside the block
234 m_Pos -= m_BlockPos + 1;
235 offset += m_BlockPos + 1;
236 m_Current = m_Current->prev;
237 m_BlockPos = m_BlockSize - (m_Current->free_space + 1);
238 }
239 }
240
241 return m_Pos;
242 }
243
244
x_SeekHEAD(CMemStore::TSize offset)245 CMemStore::TSize CMemStore::x_SeekHEAD(CMemStore::TSize offset)
246 {
247 if (offset <= 0) {
248 m_Current = m_First;
249 m_Pos = 0;
250 m_BlockPos = 0;
251 return 0;
252 }
253
254 if (offset == m_Pos)
255 return m_Pos;
256
257 if (!m_Current || (offset < m_Pos && offset < m_Pos - offset)) {
258 x_SeekHEAD(0);
259 return x_SeekCURR(offset);
260 }
261
262 return x_SeekCURR(offset - m_Pos);
263 }
264
265
x_SeekTAIL(CMemStore::TSize offset)266 CMemStore::TSize CMemStore::x_SeekTAIL(CMemStore::TSize offset)
267 {
268 if (offset >= 0) {
269 m_BlockPos = 0;
270 m_Current = 0;
271 m_Pos = m_Size;
272 return m_Pos;
273 }
274
275 return x_SeekHEAD(m_Size + offset);
276 }
277
278
Seek(long offset,EWhence whence)279 long CMemStore::Seek(long offset, EWhence whence)
280 {
281 if ( !m_Last )
282 return -1;
283
284 switch (whence) {
285 case eHead:
286 return (long) x_SeekHEAD((TSize) offset);
287 case eTail:
288 return (long) x_SeekTAIL((TSize) offset);
289 case eCurr:
290 return (long) x_SeekCURR((TSize) offset);
291 }
292
293 return -1; // error
294 }
295
296
Write(const void * buff,size_t size)297 size_t CMemStore::Write(const void* buff, size_t size)
298 {
299 if (!buff || !size)
300 return 0;
301
302 if(size > kMax_BlobSize) size= kMax_BlobSize;
303
304 char* b = (char*) buff;
305 TSize nof_bytes = (TSize) size;
306
307 TSize n = 0;
308
309 if ( m_Current ) {
310 while (nof_bytes > 0) {
311 TSize f_free = m_BlockSize - m_Current->free_space;
312
313 if ((m_BlockPos + nof_bytes) <= f_free) {
314 // we have all needed bytes in this block
315 memcpy(&m_Current->body[m_BlockPos], b + n, nof_bytes);
316 m_BlockPos += nof_bytes;
317 n += nof_bytes;
318 nof_bytes = 0;
319 if (m_BlockPos >= f_free) {
320 // we have written all bytes to this block
321 m_Current = m_Current->next;
322 m_BlockPos = 0;
323 }
324 break;
325 }
326
327 // we can write just a part to this block
328 TSize k = f_free - m_BlockPos;
329 memcpy(&m_Current->body[m_BlockPos], b + n, k);
330 n += k;
331 nof_bytes -= k;
332 m_BlockPos = 0;
333
334 m_Current = m_Current->next;
335 if ( !m_Current )
336 break;
337 }
338 }
339
340 if (nof_bytes > 0) {
341 n += static_cast<TSize>(Append(b + n, nof_bytes));
342 x_SeekTAIL(0);
343 }
344 else {
345 m_Pos += n;
346 }
347
348 return n;
349 }
350
351
Truncate(size_t size)352 size_t CMemStore::Truncate(size_t size)
353 {
354 if(size > kMax_BlobSize) size= kMax_BlobSize;
355
356 TSize nof_bytes = (TSize) size;
357
358 if (nof_bytes >= m_Size) {
359 for ( ; m_Last != NULL; m_Last = m_Current) {
360 m_Current = m_Last->prev;
361 delete [] m_Last->body;
362 delete m_Last;
363 }
364 m_First = m_Last = m_Current = 0;
365 m_BlockPos = m_Pos = m_Size = 0;
366 return 0;
367 }
368
369 while (nof_bytes > 0) {
370 TSize n = m_BlockSize - m_Last->free_space;
371 if (n <= nof_bytes) {
372 // we have to delete the whole block
373 delete [] m_Last->body;
374 SMemBlock* t = m_Last->prev;
375 _ASSERT(t != NULL);
376 t->next = NULL;
377 delete m_Last;
378 _ASSERT(m_Last != t);
379 m_Last = t;
380 nof_bytes -= n;
381 m_Size -= n;
382 continue;
383 }
384 // we have to free some bytes
385 m_Last->free_space += nof_bytes;
386 m_Size -= nof_bytes;
387 break;
388 }
389
390 if (m_Pos >= m_Size) {
391 m_Pos = m_Size;
392 m_Current = 0;
393 m_BlockPos = 0;
394 }
395
396 return m_Size;
397 }
398
399
Insert(const void * buff,size_t size)400 size_t CMemStore::Insert(const void* buff, size_t size)
401 {
402 if (!buff || !size)
403 return 0;
404
405 if(size > kMax_BlobSize) size= kMax_BlobSize;
406
407 if ( !m_Current )
408 return Append(buff, size);
409
410 char* b = (char*) buff;
411 TSize nof_bytes = (TSize) size;
412 TSize n = 0;
413
414 while (nof_bytes > 0) {
415 // first empty byte in this block
416 TSize f_free = m_BlockSize - m_Current->free_space;
417 // number of bytes to move
418 TSize k = f_free - m_BlockPos;
419
420 if (nof_bytes <= m_Current->free_space) {
421 // we can add this to existing block
422 memmove(&m_Current->body[m_BlockPos + nof_bytes],
423 &m_Current->body[m_BlockPos], k);
424 memcpy(&m_Current->body[m_BlockPos], b + n, nof_bytes);
425 m_Current->free_space -= nof_bytes;
426 n += nof_bytes;
427 m_BlockPos += nof_bytes;
428 nof_bytes = 0;
429 break;
430 }
431
432 // there is no enaugh space in existing block -- split it
433 SMemBlock* t_block = new SMemBlock;
434
435 t_block->body = new char[m_BlockSize];
436
437 t_block->next = m_Current->next;
438 if (t_block->next)
439 t_block->next->prev = t_block;
440 m_Current->next = t_block;
441 t_block->prev = m_Current;
442
443 memcpy(t_block->body, &m_Current->body[m_BlockPos], k);
444 t_block->free_space = m_BlockSize - k;
445 m_Current->free_space += k;
446
447 k = (nof_bytes <= m_Current->free_space)
448 ? nof_bytes : m_Current->free_space;
449 memcpy(&m_Current->body[m_BlockPos], b + n, k);
450 m_Current->free_space -= k;
451 nof_bytes -= k;
452 n += k;
453 if (m_Last == m_Current)
454 m_Last = t_block;
455 m_Current = t_block;
456 m_BlockPos = 0;
457 }
458 m_Pos += n;
459 m_Size += n;
460
461 // try to merge the two last blocks
462 SMemBlock* t_block = m_Current->next;
463 if (t_block != NULL
464 && (m_Current->free_space + t_block->free_space) >= m_BlockSize) {
465 TSize f_free = m_BlockSize - m_Current->free_space;
466 TSize k = m_BlockSize - t_block->free_space;
467 memcpy(&m_Current->body[f_free], t_block->body, k);
468 m_Current->free_space -= k;
469 _ASSERT(m_Current->next != t_block->next);
470 m_Current->next = t_block->next;
471 if (m_Current->next) {
472 m_Current->next->prev = m_Current;
473 } else {
474 m_Last = m_Current;
475 }
476 delete [] t_block->body;
477 delete t_block;
478 }
479 return n;
480 }
481
482
Delete(size_t size)483 size_t CMemStore::Delete(size_t size)
484 {
485 if (!m_Last || !size)
486 return m_Size;
487
488 if(size > kMax_BlobSize) size= kMax_BlobSize;
489
490 if ( !m_Current )
491 return Truncate(size);
492
493 TSize nof_bytes = (TSize) size;
494
495 if (m_BlockPos >= nof_bytes) {
496 // just one block is affected
497 memmove(&m_Current->body[m_BlockPos - nof_bytes],
498 &m_Current->body[m_BlockPos],
499 m_BlockSize - m_Current->free_space - m_BlockPos);
500 m_Current->free_space += nof_bytes;
501 m_BlockPos -= nof_bytes;
502 m_Pos -= nof_bytes;
503 m_Size -= nof_bytes;
504 return m_Size;
505 }
506
507 // we can affect several blocks...
508 if (m_BlockPos > 0) {
509 memmove(m_Current->body, &m_Current->body[m_BlockPos],
510 m_BlockSize - m_Current->free_space - m_BlockPos);
511 m_Current->free_space += m_BlockPos;
512 nof_bytes -= m_BlockPos;
513 m_Pos -= m_BlockPos;
514 m_Size -= m_BlockPos;
515 m_BlockPos = 0;
516 }
517
518 while (nof_bytes > 0) {
519 SMemBlock* t_block = m_Current->prev;
520 if ( !t_block ) {
521 m_First = m_Current;
522 break;
523 }
524
525 TSize n = m_BlockSize - t_block->free_space; // # of bytes in this block
526 if (nof_bytes < n) {
527 // all we have to delete is inside the block
528 t_block->free_space += nof_bytes;
529 m_Pos -= nof_bytes;
530 m_Size -= nof_bytes;
531 break;
532 }
533 // delete the whole block
534 if (t_block->prev)
535 t_block->prev->next = m_Current;
536 else
537 m_First = m_Current;
538
539 _ASSERT(m_Current->prev != t_block->prev);
540 m_Current->prev = t_block->prev;
541 delete [] t_block->body;
542 delete t_block;
543 m_Pos -= n;
544 m_Size -= n;
545 nof_bytes -= n;
546 }
547
548 return m_Size;
549 }
550
551
CMemStore(C_SA_Storage & storage,size_t block_size)552 CMemStore::CMemStore(C_SA_Storage& storage, size_t block_size)
553 {
554 if(block_size > kMax_BlobSize) block_size= kMax_BlobSize;
555 x_Init((TSize) block_size);
556
557 char* buff = new char[m_BlockSize];
558 TSize n;
559
560 while ((n = static_cast<TSize>(storage.Read(buff,
561 (size_t) m_BlockSize))) > 0) {
562 Append(buff, n);
563 if (n < m_BlockSize)
564 break;
565 }
566 }
567
568
~CMemStore()569 CMemStore::~CMemStore()
570 {
571 try {
572 while ( m_Last ) {
573 m_Current = m_Last->prev;
574 delete [] m_Last->body;
575 delete m_Last;
576 _ASSERT(m_Last != m_Current);
577 m_Last = m_Current;
578 }
579 }
580 NCBI_CATCH_ALL_X( 1, NCBI_CURRENT_FUNCTION )
581 }
582
583
584 END_NCBI_SCOPE
585
586
587