xref: /glogg/src/data/compressedlinestorage.cpp (revision 732308b3a6635dc95cbd6b6f5e067b954ea0ffed)
17a2f3a57SNicolas Bonnefon /*
27a2f3a57SNicolas Bonnefon  * Copyright (C) 2015 Nicolas Bonnefon and other contributors
37a2f3a57SNicolas Bonnefon  *
47a2f3a57SNicolas Bonnefon  * This file is part of glogg.
57a2f3a57SNicolas Bonnefon  *
67a2f3a57SNicolas Bonnefon  * glogg is free software: you can redistribute it and/or modify
77a2f3a57SNicolas Bonnefon  * it under the terms of the GNU General Public License as published by
87a2f3a57SNicolas Bonnefon  * the Free Software Foundation, either version 3 of the License, or
97a2f3a57SNicolas Bonnefon  * (at your option) any later version.
107a2f3a57SNicolas Bonnefon  *
117a2f3a57SNicolas Bonnefon  * glogg is distributed in the hope that it will be useful,
127a2f3a57SNicolas Bonnefon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
137a2f3a57SNicolas Bonnefon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
147a2f3a57SNicolas Bonnefon  * GNU General Public License for more details.
157a2f3a57SNicolas Bonnefon  *
167a2f3a57SNicolas Bonnefon  * You should have received a copy of the GNU General Public License
177a2f3a57SNicolas Bonnefon  * along with glogg.  If not, see <http://www.gnu.org/licenses/>.
187a2f3a57SNicolas Bonnefon  */
197a2f3a57SNicolas Bonnefon 
203c46a469SNicolas Bonnefon #include <cassert>
217a2f3a57SNicolas Bonnefon #include <cstdlib>
22*8b941e12SAnton Filimonov #include <QtEndian>
2387e663a3SNicolas Bonnefon 
249ba963cbSNicolas Bonnefon #include "utils.h"
259ba963cbSNicolas Bonnefon 
267a2f3a57SNicolas Bonnefon #include "data/compressedlinestorage.h"
277a2f3a57SNicolas Bonnefon 
287a2f3a57SNicolas Bonnefon namespace {
297a2f3a57SNicolas Bonnefon     // Functions to manipulate blocks
307a2f3a57SNicolas Bonnefon 
317a2f3a57SNicolas Bonnefon     // Create a new 32 bits block of the passed size,
327a2f3a57SNicolas Bonnefon     // initialised at the passed position
block32_new(int block_size,uint32_t initial_position,char ** block_ptr)337a2f3a57SNicolas Bonnefon     char* block32_new( int block_size, uint32_t initial_position,
347a2f3a57SNicolas Bonnefon            char** block_ptr )
357a2f3a57SNicolas Bonnefon     {
367a2f3a57SNicolas Bonnefon         // malloc a block of the maximum possible size
377a2f3a57SNicolas Bonnefon         // (where every line is >16384)
387a2f3a57SNicolas Bonnefon         char* ptr = static_cast<char*>( malloc( 4 + block_size * 6 ) );
397a2f3a57SNicolas Bonnefon 
407a2f3a57SNicolas Bonnefon         if ( ptr ) {
417a2f3a57SNicolas Bonnefon             // Write the initial_position
427a2f3a57SNicolas Bonnefon             *(reinterpret_cast<uint32_t*>(ptr)) = initial_position;
437a2f3a57SNicolas Bonnefon             *block_ptr = ptr + 4;
447a2f3a57SNicolas Bonnefon         }
457a2f3a57SNicolas Bonnefon 
467a2f3a57SNicolas Bonnefon         return ptr;
477a2f3a57SNicolas Bonnefon     }
487a2f3a57SNicolas Bonnefon 
496e2e573cSNicolas Bonnefon     // Create a new 64 bits block of the passed size,
506e2e573cSNicolas Bonnefon     // initialised at the passed position
block64_new(int block_size,uint64_t initial_position,char ** block_ptr)516e2e573cSNicolas Bonnefon     char* block64_new( int block_size, uint64_t initial_position,
526e2e573cSNicolas Bonnefon            char** block_ptr )
536e2e573cSNicolas Bonnefon     {
546e2e573cSNicolas Bonnefon         // malloc a block of the maximum possible size
556e2e573cSNicolas Bonnefon         // (where every line is >16384)
566e2e573cSNicolas Bonnefon         char* ptr = static_cast<char*>( malloc( 8 + block_size * 10 ) );
576e2e573cSNicolas Bonnefon 
586e2e573cSNicolas Bonnefon         if ( ptr ) {
596e2e573cSNicolas Bonnefon             // Write the initial_position
606e2e573cSNicolas Bonnefon             *(reinterpret_cast<uint64_t*>(ptr)) = initial_position;
616e2e573cSNicolas Bonnefon             *block_ptr = ptr + 8;
626e2e573cSNicolas Bonnefon         }
636e2e573cSNicolas Bonnefon 
646e2e573cSNicolas Bonnefon         return ptr;
656e2e573cSNicolas Bonnefon     }
666e2e573cSNicolas Bonnefon 
676e2e573cSNicolas Bonnefon 
687a2f3a57SNicolas Bonnefon     // Add a one byte relative delta (0-127) and inc pointer
697a2f3a57SNicolas Bonnefon     // First bit is always 0
block_add_one_byte_relative(char ** ptr,uint8_t value)707a2f3a57SNicolas Bonnefon     void block_add_one_byte_relative( char** ptr, uint8_t value )
717a2f3a57SNicolas Bonnefon     {
727a2f3a57SNicolas Bonnefon         **ptr = value;
737a2f3a57SNicolas Bonnefon         *ptr += sizeof( value );
747a2f3a57SNicolas Bonnefon     }
757a2f3a57SNicolas Bonnefon 
767a2f3a57SNicolas Bonnefon     // Add a two bytes relative delta (0-16383) and inc pointer
777a2f3a57SNicolas Bonnefon     // First 2 bits are always 10
block_add_two_bytes_relative(char ** ptr,uint16_t value)787a2f3a57SNicolas Bonnefon     void block_add_two_bytes_relative( char** ptr, uint16_t value )
797a2f3a57SNicolas Bonnefon     {
807a2f3a57SNicolas Bonnefon         // Stored in big endian format in order to recognise the initial pattern:
817a2f3a57SNicolas Bonnefon         // 10xx xxxx xxxx xxxx
827a2f3a57SNicolas Bonnefon         //  HO byte | LO byte
83*8b941e12SAnton Filimonov         *(reinterpret_cast<uint16_t*>(*ptr)) = qToBigEndian( static_cast<uint16_t>( value | (1 << 15) ) );
847a2f3a57SNicolas Bonnefon         *ptr += sizeof( value );
857a2f3a57SNicolas Bonnefon     }
867a2f3a57SNicolas Bonnefon 
877a2f3a57SNicolas Bonnefon     // Add a signal and a 4 bytes absolute position and inc pointer
block32_add_absolute(char ** ptr,uint32_t value)887a2f3a57SNicolas Bonnefon     void block32_add_absolute( char** ptr, uint32_t value )
897a2f3a57SNicolas Bonnefon     {
907a2f3a57SNicolas Bonnefon         // 2 bytes marker (actually only the first two bits are tested)
917a2f3a57SNicolas Bonnefon         *(reinterpret_cast<uint16_t*>(*ptr)) = 0xFF;
927a2f3a57SNicolas Bonnefon         *ptr += sizeof( uint16_t );
937a2f3a57SNicolas Bonnefon         // Absolute value (machine endian)
947a2f3a57SNicolas Bonnefon         *(reinterpret_cast<uint32_t*>(*ptr)) = value;
957a2f3a57SNicolas Bonnefon         *ptr += sizeof( uint32_t );
967a2f3a57SNicolas Bonnefon     }
977a2f3a57SNicolas Bonnefon 
987a2f3a57SNicolas Bonnefon     // Initialise the passed block for reading, returning
997a2f3a57SNicolas Bonnefon     // the initial position and a pointer to the second entry.
block32_initial_pos(char * block,char ** ptr)1007a2f3a57SNicolas Bonnefon     uint64_t block32_initial_pos( char* block, char** ptr )
1017a2f3a57SNicolas Bonnefon     {
1027a2f3a57SNicolas Bonnefon         *ptr = block + sizeof( uint32_t );
1037a2f3a57SNicolas Bonnefon         return *(reinterpret_cast<uint32_t*>(block));
1047a2f3a57SNicolas Bonnefon     }
1057a2f3a57SNicolas Bonnefon 
1067a2f3a57SNicolas Bonnefon     // Give the next position in the block based on the previous
1077a2f3a57SNicolas Bonnefon     // position, then increase the pointer.
block32_next_pos(char ** ptr,uint64_t previous_pos)1087a2f3a57SNicolas Bonnefon     uint64_t block32_next_pos( char** ptr, uint64_t previous_pos )
1097a2f3a57SNicolas Bonnefon     {
1107a2f3a57SNicolas Bonnefon         uint64_t pos = previous_pos;
1117a2f3a57SNicolas Bonnefon 
1127a2f3a57SNicolas Bonnefon         uint8_t byte = **ptr;
1137a2f3a57SNicolas Bonnefon         ++(*ptr);
1147a2f3a57SNicolas Bonnefon         if ( ! ( byte & 0x80 ) ) {
1157a2f3a57SNicolas Bonnefon             // High order bit is 0
1167a2f3a57SNicolas Bonnefon             pos += byte;
1177a2f3a57SNicolas Bonnefon         }
1187a2f3a57SNicolas Bonnefon         else if ( ( byte & 0xC0 ) == 0x80 ) {
1197a2f3a57SNicolas Bonnefon             // We need to read the low order byte
1207a2f3a57SNicolas Bonnefon             uint8_t lo_byte = **ptr;
1217a2f3a57SNicolas Bonnefon             ++(*ptr);
1227a2f3a57SNicolas Bonnefon             // Remove the starting 10b
1237a2f3a57SNicolas Bonnefon             byte &= ~0xC0;
1247a2f3a57SNicolas Bonnefon             // And form the displacement (stored as big endian)
1257a2f3a57SNicolas Bonnefon             pos += ( (uint16_t) byte << 8 ) | (uint16_t) lo_byte;
1267a2f3a57SNicolas Bonnefon         }
1277a2f3a57SNicolas Bonnefon         else {
1287a2f3a57SNicolas Bonnefon             // Skip the next byte (not used)
1297a2f3a57SNicolas Bonnefon             ++(*ptr);
1307a2f3a57SNicolas Bonnefon             // And read the new absolute pos (machine endian)
1317a2f3a57SNicolas Bonnefon             pos = *(reinterpret_cast<uint32_t*>(*ptr));
1327a2f3a57SNicolas Bonnefon             *ptr += sizeof( uint32_t );
1337a2f3a57SNicolas Bonnefon         }
1347a2f3a57SNicolas Bonnefon 
1357a2f3a57SNicolas Bonnefon         return pos;
1367a2f3a57SNicolas Bonnefon     }
1376e2e573cSNicolas Bonnefon 
1386e2e573cSNicolas Bonnefon     // Add a signal and a 8 bytes absolute position and inc pointer
block64_add_absolute(char ** ptr,uint64_t value)1396e2e573cSNicolas Bonnefon     void block64_add_absolute( char** ptr, uint64_t value )
1406e2e573cSNicolas Bonnefon     {
1416e2e573cSNicolas Bonnefon         // This is unaligned, can cause problem on some CPUs
1426e2e573cSNicolas Bonnefon 
1436e2e573cSNicolas Bonnefon         // 2 bytes marker (actually only the first two bits are tested)
1446e2e573cSNicolas Bonnefon         *(reinterpret_cast<uint16_t*>(*ptr)) = 0xFF;
1456e2e573cSNicolas Bonnefon         *ptr += sizeof( uint16_t );
1466e2e573cSNicolas Bonnefon         // Absolute value (machine endian)
1476e2e573cSNicolas Bonnefon         *(reinterpret_cast<uint64_t*>(*ptr)) = value;
1486e2e573cSNicolas Bonnefon         *ptr += sizeof( uint64_t );
1496e2e573cSNicolas Bonnefon     }
1506e2e573cSNicolas Bonnefon 
1516e2e573cSNicolas Bonnefon     // Initialise the passed block for reading, returning
1526e2e573cSNicolas Bonnefon     // the initial position and a pointer to the second entry.
block64_initial_pos(char * block,char ** ptr)1536e2e573cSNicolas Bonnefon     uint64_t block64_initial_pos( char* block, char** ptr )
1546e2e573cSNicolas Bonnefon     {
1556e2e573cSNicolas Bonnefon         *ptr = block + sizeof( uint64_t );
1566e2e573cSNicolas Bonnefon         return *(reinterpret_cast<uint64_t*>(block));
1576e2e573cSNicolas Bonnefon     }
1586e2e573cSNicolas Bonnefon 
1596e2e573cSNicolas Bonnefon     // Give the next position in the block based on the previous
1606e2e573cSNicolas Bonnefon     // position, then increase the pointer.
block64_next_pos(char ** ptr,uint64_t previous_pos)1616e2e573cSNicolas Bonnefon     uint64_t block64_next_pos( char** ptr, uint64_t previous_pos )
1626e2e573cSNicolas Bonnefon     {
1636e2e573cSNicolas Bonnefon         uint64_t pos = previous_pos;
1646e2e573cSNicolas Bonnefon 
1656e2e573cSNicolas Bonnefon         uint8_t byte = **ptr;
1666e2e573cSNicolas Bonnefon         ++(*ptr);
1676e2e573cSNicolas Bonnefon         if ( ! ( byte & 0x80 ) ) {
1686e2e573cSNicolas Bonnefon             // High order bit is 0
1696e2e573cSNicolas Bonnefon             pos += byte;
1706e2e573cSNicolas Bonnefon         }
1716e2e573cSNicolas Bonnefon         else if ( ( byte & 0xC0 ) == 0x80 ) {
1726e2e573cSNicolas Bonnefon             // We need to read the low order byte
1736e2e573cSNicolas Bonnefon             uint8_t lo_byte = **ptr;
1746e2e573cSNicolas Bonnefon             ++(*ptr);
1756e2e573cSNicolas Bonnefon             // Remove the starting 10b
1766e2e573cSNicolas Bonnefon             byte &= ~0xC0;
1776e2e573cSNicolas Bonnefon             // And form the displacement (stored as big endian)
1786e2e573cSNicolas Bonnefon             pos += ( (uint16_t) byte << 8 ) | (uint16_t) lo_byte;
1796e2e573cSNicolas Bonnefon         }
1806e2e573cSNicolas Bonnefon         else {
1816e2e573cSNicolas Bonnefon             // Skip the next byte (not used)
1826e2e573cSNicolas Bonnefon             ++(*ptr);
1836e2e573cSNicolas Bonnefon             // And read the new absolute pos (machine endian)
1846e2e573cSNicolas Bonnefon             pos = *(reinterpret_cast<uint64_t*>(*ptr));
1856e2e573cSNicolas Bonnefon             *ptr += sizeof( uint64_t );
1866e2e573cSNicolas Bonnefon         }
1876e2e573cSNicolas Bonnefon 
1886e2e573cSNicolas Bonnefon         return pos;
1896e2e573cSNicolas Bonnefon     }
1907a2f3a57SNicolas Bonnefon }
1917a2f3a57SNicolas Bonnefon 
move_from(CompressedLinePositionStorage && orig)1923c46a469SNicolas Bonnefon void CompressedLinePositionStorage::move_from(
1933c46a469SNicolas Bonnefon         CompressedLinePositionStorage&& orig )
1943c46a469SNicolas Bonnefon {
1953c46a469SNicolas Bonnefon     nb_lines_        = orig.nb_lines_;
1963c46a469SNicolas Bonnefon     first_long_line_ = orig.first_long_line_;
1973c46a469SNicolas Bonnefon     current_pos_     = orig.current_pos_;
1983c46a469SNicolas Bonnefon     block_pointer_   = orig.block_pointer_;
1993c46a469SNicolas Bonnefon     previous_block_pointer_ = orig.previous_block_pointer_;
2003c46a469SNicolas Bonnefon 
2013c46a469SNicolas Bonnefon     orig.nb_lines_   = 0;
2023c46a469SNicolas Bonnefon }
2033c46a469SNicolas Bonnefon 
2043c46a469SNicolas Bonnefon // Move constructor
CompressedLinePositionStorage(CompressedLinePositionStorage && orig)2053c46a469SNicolas Bonnefon CompressedLinePositionStorage::CompressedLinePositionStorage(
2063c46a469SNicolas Bonnefon         CompressedLinePositionStorage&& orig )
207fcf40f17SNicolas Bonnefon     : block32_index_( std::move( orig.block32_index_ ) ),
208fcf40f17SNicolas Bonnefon       block64_index_( std::move( orig.block64_index_ ) )
2093c46a469SNicolas Bonnefon {
2103c46a469SNicolas Bonnefon     move_from( std::move( orig ) );
2113c46a469SNicolas Bonnefon }
2123c46a469SNicolas Bonnefon 
free_blocks()21325d0072cSNicolas Bonnefon void CompressedLinePositionStorage::free_blocks()
21425d0072cSNicolas Bonnefon {
21525d0072cSNicolas Bonnefon     for ( char* block : block32_index_ ) {
21625d0072cSNicolas Bonnefon         void* p = static_cast<void*>( block );
21725d0072cSNicolas Bonnefon         free( p );
21825d0072cSNicolas Bonnefon     }
21925d0072cSNicolas Bonnefon 
22025d0072cSNicolas Bonnefon     for ( char* block : block64_index_ ) {
22125d0072cSNicolas Bonnefon         void* p = static_cast<void*>( block );
22225d0072cSNicolas Bonnefon         free( p );
22325d0072cSNicolas Bonnefon     }
22425d0072cSNicolas Bonnefon }
22525d0072cSNicolas Bonnefon 
2263c46a469SNicolas Bonnefon // Move assignement
operator =(CompressedLinePositionStorage && orig)2273c46a469SNicolas Bonnefon CompressedLinePositionStorage& CompressedLinePositionStorage::operator=(
2283c46a469SNicolas Bonnefon         CompressedLinePositionStorage&& orig )
2293c46a469SNicolas Bonnefon {
23025d0072cSNicolas Bonnefon     free_blocks();
23125d0072cSNicolas Bonnefon 
2323c46a469SNicolas Bonnefon     block32_index_ = std::move( orig.block32_index_ );
233fcf40f17SNicolas Bonnefon     block64_index_ = std::move( orig.block64_index_ );
2343c46a469SNicolas Bonnefon     move_from( std::move( orig ) );
2353c46a469SNicolas Bonnefon 
2363c46a469SNicolas Bonnefon     return *this;
2373c46a469SNicolas Bonnefon }
2383c46a469SNicolas Bonnefon 
~CompressedLinePositionStorage()2393c46a469SNicolas Bonnefon CompressedLinePositionStorage::~CompressedLinePositionStorage()
2403c46a469SNicolas Bonnefon {
24125d0072cSNicolas Bonnefon     free_blocks();
2423c46a469SNicolas Bonnefon }
2433c46a469SNicolas Bonnefon 
2447a2f3a57SNicolas Bonnefon // template<int BLOCK_SIZE>
append(uint64_t pos)2457a2f3a57SNicolas Bonnefon void CompressedLinePositionStorage::append( uint64_t pos )
2467a2f3a57SNicolas Bonnefon {
247fcf40f17SNicolas Bonnefon     // Lines must be stored in order
248fcf40f17SNicolas Bonnefon     assert( ( pos > current_pos_ ) || ( pos == 0 ) );
249fcf40f17SNicolas Bonnefon 
2503c46a469SNicolas Bonnefon     // Save the pointer in case we need to "pop_back"
2513c46a469SNicolas Bonnefon     previous_block_pointer_ = block_pointer_;
2523c46a469SNicolas Bonnefon 
2536e2e573cSNicolas Bonnefon     bool store_in_big = false;
2546e2e573cSNicolas Bonnefon     if ( pos > UINT32_MAX ) {
2556e2e573cSNicolas Bonnefon         store_in_big = true;
2566e2e573cSNicolas Bonnefon         if ( first_long_line_ == UINT32_MAX ) {
2576e2e573cSNicolas Bonnefon             // First "big" end of line, we will start a new (64) block
2586e2e573cSNicolas Bonnefon             first_long_line_ = nb_lines_;
2596e2e573cSNicolas Bonnefon             block_pointer_ = nullptr;
2606e2e573cSNicolas Bonnefon         }
2616e2e573cSNicolas Bonnefon     }
2626e2e573cSNicolas Bonnefon 
2637a2f3a57SNicolas Bonnefon     if ( ! block_pointer_ ) {
2647a2f3a57SNicolas Bonnefon         // We need to start a new block
2656e2e573cSNicolas Bonnefon         if ( ! store_in_big )
2667a2f3a57SNicolas Bonnefon             block32_index_.push_back(
2677a2f3a57SNicolas Bonnefon                 block32_new( BLOCK_SIZE, pos, &block_pointer_ ) );
2686e2e573cSNicolas Bonnefon         else
2696e2e573cSNicolas Bonnefon             block64_index_.push_back(
2706e2e573cSNicolas Bonnefon                 block64_new( BLOCK_SIZE, pos, &block_pointer_ ) );
2717a2f3a57SNicolas Bonnefon     }
2727a2f3a57SNicolas Bonnefon     else {
2737a2f3a57SNicolas Bonnefon         uint64_t delta = pos - current_pos_;
2747a2f3a57SNicolas Bonnefon         if ( delta < 128 ) {
2757a2f3a57SNicolas Bonnefon             // Code relative on one byte
2767a2f3a57SNicolas Bonnefon             block_add_one_byte_relative( &block_pointer_, delta );
2777a2f3a57SNicolas Bonnefon         }
2787a2f3a57SNicolas Bonnefon         else if ( delta < 16384 ) {
2797a2f3a57SNicolas Bonnefon             // Code relative on two bytes
2807a2f3a57SNicolas Bonnefon             block_add_two_bytes_relative( &block_pointer_, delta );
2817a2f3a57SNicolas Bonnefon         }
2827a2f3a57SNicolas Bonnefon         else {
2837a2f3a57SNicolas Bonnefon             // Code absolute
2846e2e573cSNicolas Bonnefon             if ( ! store_in_big )
2857a2f3a57SNicolas Bonnefon                 block32_add_absolute( &block_pointer_, pos );
2866e2e573cSNicolas Bonnefon             else
2876e2e573cSNicolas Bonnefon                 block64_add_absolute( &block_pointer_, pos );
2887a2f3a57SNicolas Bonnefon         }
2897a2f3a57SNicolas Bonnefon     }
2907a2f3a57SNicolas Bonnefon 
2917a2f3a57SNicolas Bonnefon     current_pos_ = pos;
2927a2f3a57SNicolas Bonnefon     ++nb_lines_;
2937a2f3a57SNicolas Bonnefon 
2946e2e573cSNicolas Bonnefon     if ( ! store_in_big ) {
2957a2f3a57SNicolas Bonnefon         if ( nb_lines_ % BLOCK_SIZE == 0 ) {
2967a2f3a57SNicolas Bonnefon             // We have finished the block
29787e663a3SNicolas Bonnefon 
29887e663a3SNicolas Bonnefon             // Let's reduce its size to what is actually used
29987e663a3SNicolas Bonnefon             int block_index = nb_lines_ / BLOCK_SIZE - 1;
30087e663a3SNicolas Bonnefon             char* block = block32_index_[block_index];
30187e663a3SNicolas Bonnefon             // We allocate extra space for the last element in case it
30287e663a3SNicolas Bonnefon             // is replaced by an absolute value in the future (following a pop_back)
30387e663a3SNicolas Bonnefon             size_t new_size = ( previous_block_pointer_
30487e663a3SNicolas Bonnefon                     + sizeof( uint16_t ) + sizeof( uint32_t ) ) - block;
30587e663a3SNicolas Bonnefon             void* new_location = realloc( block, new_size );
30687e663a3SNicolas Bonnefon             if ( new_location )
30787e663a3SNicolas Bonnefon                 block32_index_[block_index] = static_cast<char*>( new_location );
30887e663a3SNicolas Bonnefon 
3097a2f3a57SNicolas Bonnefon             block_pointer_ = nullptr;
31005467f52SNicolas Bonnefon             previous_block_pointer_ = static_cast<char*>( new_location ) + ( previous_block_pointer_ - block );
3117a2f3a57SNicolas Bonnefon         }
3127a2f3a57SNicolas Bonnefon     }
3136e2e573cSNicolas Bonnefon     else {
3146e2e573cSNicolas Bonnefon         if ( ( nb_lines_ - first_long_line_ ) % BLOCK_SIZE == 0 ) {
3156e2e573cSNicolas Bonnefon             // We have finished the block
3166e2e573cSNicolas Bonnefon 
3176e2e573cSNicolas Bonnefon             // Let's reduce its size to what is actually used
3186e2e573cSNicolas Bonnefon             int block_index = ( nb_lines_ - first_long_line_ ) / BLOCK_SIZE - 1;
3196e2e573cSNicolas Bonnefon             char* block = block64_index_[block_index];
3206e2e573cSNicolas Bonnefon             // We allocate extra space for the last element in case it
3216e2e573cSNicolas Bonnefon             // is replaced by an absolute value in the future (following a pop_back)
3226e2e573cSNicolas Bonnefon             size_t new_size = ( previous_block_pointer_
3236e2e573cSNicolas Bonnefon                     + sizeof( uint16_t ) + sizeof( uint64_t ) ) - block;
3246e2e573cSNicolas Bonnefon             void* new_location = realloc( block, new_size );
3256e2e573cSNicolas Bonnefon             if ( new_location )
3266e2e573cSNicolas Bonnefon                 block64_index_[block_index] = static_cast<char*>( new_location );
3276e2e573cSNicolas Bonnefon 
3286e2e573cSNicolas Bonnefon             block_pointer_ = nullptr;
32905467f52SNicolas Bonnefon             previous_block_pointer_ = static_cast<char*>( new_location ) + ( previous_block_pointer_ - block );
3306e2e573cSNicolas Bonnefon         }
3316e2e573cSNicolas Bonnefon     }
3326e2e573cSNicolas Bonnefon }
3337a2f3a57SNicolas Bonnefon 
3347a2f3a57SNicolas Bonnefon // template<int BLOCK_SIZE>
at(uint32_t index) const3357151e3acSNicolas Bonnefon uint64_t CompressedLinePositionStorage::at( uint32_t index ) const
3367a2f3a57SNicolas Bonnefon {
3377a2f3a57SNicolas Bonnefon     char* ptr;
3387151e3acSNicolas Bonnefon     uint64_t position;
3397a2f3a57SNicolas Bonnefon 
3407151e3acSNicolas Bonnefon     Cache* last_read = last_read_.getPtr();
3417151e3acSNicolas Bonnefon 
3426e2e573cSNicolas Bonnefon     if ( index < first_long_line_ ) {
3437151e3acSNicolas Bonnefon         if ( ( index == last_read->index + 1 ) && ( index % BLOCK_SIZE != 0 ) ) {
3447151e3acSNicolas Bonnefon             position = last_read->position;
3457151e3acSNicolas Bonnefon             ptr      = last_read->ptr;
3467151e3acSNicolas Bonnefon 
3477151e3acSNicolas Bonnefon             position = block32_next_pos( &ptr, position );
3487151e3acSNicolas Bonnefon         }
3497151e3acSNicolas Bonnefon         else {
3507151e3acSNicolas Bonnefon             char* block = block32_index_[ index / BLOCK_SIZE ];
3517151e3acSNicolas Bonnefon             position = block32_initial_pos( block, &ptr );
3527151e3acSNicolas Bonnefon 
3537151e3acSNicolas Bonnefon             for ( uint32_t i = 0; i < index % BLOCK_SIZE; i++ ) {
3547a2f3a57SNicolas Bonnefon                 // Go through all the lines in the block till the one we want
3557a2f3a57SNicolas Bonnefon                 position = block32_next_pos( &ptr, position );
3567a2f3a57SNicolas Bonnefon             }
3577151e3acSNicolas Bonnefon         }
3586e2e573cSNicolas Bonnefon     }
3596e2e573cSNicolas Bonnefon     else {
3606e2e573cSNicolas Bonnefon         const uint32_t index_in_64 = index - first_long_line_;
3616e2e573cSNicolas Bonnefon         if ( ( index == last_read->index + 1 ) && ( index_in_64 % BLOCK_SIZE != 0 ) ) {
3626e2e573cSNicolas Bonnefon             position = last_read->position;
3636e2e573cSNicolas Bonnefon             ptr      = last_read->ptr;
3646e2e573cSNicolas Bonnefon 
3656e2e573cSNicolas Bonnefon             position = block64_next_pos( &ptr, position );
3666e2e573cSNicolas Bonnefon         }
3676e2e573cSNicolas Bonnefon         else {
3686e2e573cSNicolas Bonnefon             char* block = block64_index_[ index_in_64 / BLOCK_SIZE ];
3696e2e573cSNicolas Bonnefon             position = block64_initial_pos( block, &ptr );
3706e2e573cSNicolas Bonnefon 
3716e2e573cSNicolas Bonnefon             for ( uint32_t i = 0; i < index_in_64 % BLOCK_SIZE; i++ ) {
3726e2e573cSNicolas Bonnefon                 // Go through all the lines in the block till the one we want
3736e2e573cSNicolas Bonnefon                 position = block64_next_pos( &ptr, position );
3746e2e573cSNicolas Bonnefon             }
3756e2e573cSNicolas Bonnefon         }
3766e2e573cSNicolas Bonnefon     }
3777151e3acSNicolas Bonnefon 
3787151e3acSNicolas Bonnefon     // Populate our cache ready for next consecutive read
3797151e3acSNicolas Bonnefon     last_read->index    = index;
3807151e3acSNicolas Bonnefon     last_read->position = position;
3817151e3acSNicolas Bonnefon     last_read->ptr      = ptr;
3827a2f3a57SNicolas Bonnefon 
3837a2f3a57SNicolas Bonnefon     return position;
3847a2f3a57SNicolas Bonnefon }
3857a2f3a57SNicolas Bonnefon 
append_list(const std::vector<uint64_t> & positions)3867a2f3a57SNicolas Bonnefon void CompressedLinePositionStorage::append_list(
387e49f4922SNicolas Bonnefon         const std::vector<uint64_t>& positions )
3887a2f3a57SNicolas Bonnefon {
3897a2f3a57SNicolas Bonnefon     // This is not very clever, but caching should make it
3907a2f3a57SNicolas Bonnefon     // reasonably fast.
391e49f4922SNicolas Bonnefon     for ( uint32_t i = 0; i < positions.size(); i++ )
392e49f4922SNicolas Bonnefon         append( positions.at( i ) );
3937a2f3a57SNicolas Bonnefon }
3947a2f3a57SNicolas Bonnefon 
3957a2f3a57SNicolas Bonnefon // template<int BLOCK_SIZE>
pop_back()3967a2f3a57SNicolas Bonnefon void CompressedLinePositionStorage::pop_back()
3977a2f3a57SNicolas Bonnefon {
3983c46a469SNicolas Bonnefon     // Removing the last entered data, there are two cases
3993c46a469SNicolas Bonnefon     if ( previous_block_pointer_ ) {
4003c46a469SNicolas Bonnefon         // The last append was a normal entry in an existing block,
4013c46a469SNicolas Bonnefon         // so we can just revert the pointer
4023c46a469SNicolas Bonnefon         block_pointer_ = previous_block_pointer_;
4033c46a469SNicolas Bonnefon         previous_block_pointer_ = nullptr;
4043c46a469SNicolas Bonnefon     }
4053c46a469SNicolas Bonnefon     else {
4063c46a469SNicolas Bonnefon         // A new block has been created for the last entry, we need
4073c46a469SNicolas Bonnefon         // to de-alloc it.
4083c46a469SNicolas Bonnefon 
409fcf40f17SNicolas Bonnefon         if ( first_long_line_ == UINT32_MAX ) {
4103c46a469SNicolas Bonnefon             // If we try to pop_back() twice, we're dead!
4113c46a469SNicolas Bonnefon             assert( ( nb_lines_ - 1 ) % BLOCK_SIZE == 0 );
4123c46a469SNicolas Bonnefon 
4133c46a469SNicolas Bonnefon             char* block = block32_index_.back();
4143c46a469SNicolas Bonnefon             block32_index_.pop_back();
4153c46a469SNicolas Bonnefon             free( block );
416fcf40f17SNicolas Bonnefon         }
417fcf40f17SNicolas Bonnefon         else {
418fcf40f17SNicolas Bonnefon             // If we try to pop_back() twice, we're dead!
419fcf40f17SNicolas Bonnefon             assert( ( nb_lines_ - first_long_line_ - 1 ) % BLOCK_SIZE == 0 );
420fcf40f17SNicolas Bonnefon 
421fcf40f17SNicolas Bonnefon             char* block = block64_index_.back();
422fcf40f17SNicolas Bonnefon             block64_index_.pop_back();
423fcf40f17SNicolas Bonnefon             free( block );
424fcf40f17SNicolas Bonnefon         }
4253c46a469SNicolas Bonnefon 
4263c46a469SNicolas Bonnefon         block_pointer_ = nullptr;
4273c46a469SNicolas Bonnefon     }
4283c46a469SNicolas Bonnefon 
4293c46a469SNicolas Bonnefon     --nb_lines_;
4303c46a469SNicolas Bonnefon     current_pos_ = at( nb_lines_ - 1 );
4317a2f3a57SNicolas Bonnefon }
432