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