/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
Copyright ( c ) 2020 , MariaDB Corporation .
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; version 2 of the License .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
/* Functions to compressed records */
#include "fulltext.h"
#define IS_CHAR ((uint) 32768 ) /* Bit if char (not offset) in tree */
/* Some definitions to keep in sync with myisampack.c */
#define HEAD_LENGTH 32 /* Length of fixed header */
#if INT_MAX > 32767
#define BITS_SAVED 32
#define MAX_QUICK_TABLE_BITS 9 /* Because we may shift in 24 bits */
#else
#define BITS_SAVED 16
#define MAX_QUICK_TABLE_BITS 6
#endif
#define get_bit(BU) ((BU)->bits ? \
(BU)->current_byte & ((mi_bit_type) 1 << --(BU)->bits) :\
(fill_buffer(BU), (BU)->bits= BITS_SAVED-1 ,\
(BU)->current_byte & ((mi_bit_type) 1 << (BITS_SAVED-1 ))))
#define skip_to_next_byte(BU) ((BU)->bits&=~7 )
#define get_bits(BU,count) (((BU)->bits >= count) ? (((BU)->current_byte >> ((BU)->bits-=count)) & mask[count]) : fill_and_get_bits(BU,count))
#define decode_bytes_test_bit(bit) \
if (low_byte & (1 << (7 -bit))) \
pos++; \
if (*pos & IS_CHAR) \
{ bits-=(bit+1 ); break ; } \
pos+= *pos
/* Size in uint16 of a Huffman tree for byte compression of 256 byte values. */
#define OFFSET_TABLE_SIZE 512
static uint read_huff_table(MI_BIT_BUFF *bit_buff,MI_DECODE_TREE *decode_tree,
uint16 **decode_table,uchar **intervall_buff,
uint16 *tmp_buff);
static void make_quick_table(uint16 *to_table,uint16 *decode_table,
uint *next_free,uint value,uint bits,
uint max_bits);
static void fill_quick_table(uint16 *table,uint bits, uint max_bits,
uint value);
static uint copy_decode_table(uint16 *to_pos,uint offset,
uint16 *decode_table);
static uint find_longest_bitstream(uint16 *table, uint16 *end);
static void (*get_unpack_function(MI_COLUMNDEF *rec))(MI_COLUMNDEF *field,
MI_BIT_BUFF *buff,
uchar *to,
uchar *end);
static void uf_zerofill_skip_zero(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_skip_zero(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_space_normal(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_space_endspace_selected(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end);
static void uf_endspace_selected(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_space_endspace(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_endspace(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_space_prespace_selected(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end);
static void uf_prespace_selected(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_space_prespace(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_prespace(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_zerofill_normal(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_constant(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_intervall(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_zero(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static void uf_blob(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end);
static void uf_varchar1(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end);
static void uf_varchar2(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end);
static void decode_bytes(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,
uchar *to,uchar *end);
static uint decode_pos(MI_BIT_BUFF *bit_buff,MI_DECODE_TREE *decode_tree);
static void init_bit_buffer(MI_BIT_BUFF *bit_buff,uchar *buffer,uint length);
static uint fill_and_get_bits(MI_BIT_BUFF *bit_buff,uint count);
static void fill_buffer(MI_BIT_BUFF *bit_buff);
static uint max_bit(uint value);
static uint read_pack_length(uint version, const uchar *buf, ulong *length);
#ifdef HAVE_MMAP
static uchar *_mi_mempack_get_block_info(MI_INFO *myisam, MI_BIT_BUFF *bit_buff,
MI_BLOCK_INFO *info, uchar **rec_buff_p,
uchar *header);
#endif
static mi_bit_type mask[]=
{
0 x00000000,
0 x00000001, 0 x00000003, 0 x00000007, 0 x0000000f,
0 x0000001f, 0 x0000003f, 0 x0000007f, 0 x000000ff,
0 x000001ff, 0 x000003ff, 0 x000007ff, 0 x00000fff,
0 x00001fff, 0 x00003fff, 0 x00007fff, 0 x0000ffff,
#if BITS_SAVED > 16
0 x0001ffff, 0 x0003ffff, 0 x0007ffff, 0 x000fffff,
0 x001fffff, 0 x003fffff, 0 x007fffff, 0 x00ffffff,
0 x01ffffff, 0 x03ffffff, 0 x07ffffff, 0 x0fffffff,
0 x1fffffff, 0 x3fffffff, 0 x7fffffff, 0 xffffffff,
#endif
};
/* Read all packed info, allocate memory and fix field structs */
my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys)
{
File file;
int diff_length;
uint i,trees,huff_tree_bits,rec_reflength,length;
uint16 *decode_table,*tmp_buff;
ulong elements,intervall_length;
uchar *disk_cache;
uchar *intervall_buff;
uchar header[HEAD_LENGTH];
MYISAM_SHARE *share=info->s;
MI_BIT_BUFF bit_buff;
DBUG_ENTER("_mi_read_pack_info" );
if (myisam_quick_table_bits < 4 )
myisam_quick_table_bits=4 ;
else if (myisam_quick_table_bits > MAX_QUICK_TABLE_BITS)
myisam_quick_table_bits=MAX_QUICK_TABLE_BITS;
file=info->dfile;
my_errno=0 ;
if (mysql_file_read(file, (uchar*) header, sizeof (header), MYF(MY_NABP)))
{
if (!my_errno)
my_errno=HA_ERR_END_OF_FILE;
goto err0;
}
/* Only the first three bytes of magic number are independent of version. */
if (memcmp((uchar*) header, (uchar*) myisam_pack_file_magic, 3 ))
{
my_errno=HA_ERR_WRONG_IN_RECORD;
goto err0;
}
share->pack .version= header[3 ]; /* fourth byte of magic number */
share->pack .header_length= uint4korr(header+4 );
share->min_pack_length=(uint) uint4korr(header+8 );
share->max_pack_length=(uint) uint4korr(header+12 );
elements=uint4korr(header+16 );
intervall_length=uint4korr(header+20 );
trees=uint2korr(header+24 );
share->pack .ref_length=header[26 ];
rec_reflength=header[27 ];
diff_length=(int ) rec_reflength - (int ) share->base.rec_reflength;
if (fix_keys)
share->rec_reflength=rec_reflength;
share->base.min_block_length=share->min_pack_length+1 ;
if (share->min_pack_length > 254 )
share->base.min_block_length+=2 ;
DBUG_PRINT("info" , ("fixed header length: %u" , HEAD_LENGTH));
DBUG_PRINT("info" , ("total header length: %lu" , share->pack .header_length));
DBUG_PRINT("info" , ("pack file version: %u" , share->pack .version));
DBUG_PRINT("info" , ("min pack length: %lu" , share->min_pack_length));
DBUG_PRINT("info" , ("max pack length: %lu" , share->max_pack_length));
DBUG_PRINT("info" , ("elements of all trees: %lu" , elements));
DBUG_PRINT("info" , ("distinct values bytes: %lu" , intervall_length));
DBUG_PRINT("info" , ("number of code trees: %u" , trees));
DBUG_PRINT("info" , ("bytes for record lgt: %u" , share->pack .ref_length));
DBUG_PRINT("info" , ("record pointer length: %u" , rec_reflength));
/*
Memory segment # 1 :
- Decode tree heads
- Distinct column values
*/
if (!(share->decode_trees=(MI_DECODE_TREE*)
my_malloc(mi_key_memory_MI_DECODE_TREE,
trees*sizeof (MI_DECODE_TREE) + intervall_length*sizeof (uchar),
MYF(MY_WME))))
goto err0;
intervall_buff=(uchar*) (share->decode_trees+trees);
/*
Memory segment # 2 :
- Decode tables
- Quick decode tables
- Temporary decode table
- Compressed data file header cache
This segment will be reallocated after construction of the tables .
*/
length=(uint) (elements*2 +trees*(1 << myisam_quick_table_bits));
/*
To keep some algorithms simpler , we accept that they access
bytes beyond the end of the input data . This can affect up to
one byte less than the " word size " size used in this file ,
which is BITS_SAVED / 8 . To avoid accessing non - allocated
data , we add ( BITS_SAVED / 8 ) - 1 bytes to the buffer size .
*/
if (!(share->decode_tables=(uint16*)
my_malloc(mi_key_memory_MYISAM_SHARE_decode_tables,
(length + OFFSET_TABLE_SIZE) * sizeof (uint16) +
(uint) (share->pack .header_length - sizeof (header) +
(BITS_SAVED / 8 ) - 1 ), MYF(MY_WME | MY_ZEROFILL))))
goto err1;
tmp_buff=share->decode_tables+length;
disk_cache= (uchar*) (tmp_buff+OFFSET_TABLE_SIZE);
if (mysql_file_read(file, disk_cache,
(uint) (share->pack .header_length-sizeof (header)),
MYF(MY_NABP)))
goto err2;
huff_tree_bits=max_bit(trees ? trees-1 : 0 );
init_bit_buffer(&bit_buff, disk_cache,
(uint) (share->pack .header_length-sizeof (header)));
/* Read new info for each field */
for (i=0 ; i < share->base.fields ; i++)
{
share->rec[i].base_type=(enum en_fieldtype) get_bits(&bit_buff,5 );
share->rec[i].pack_type=(uint) get_bits(&bit_buff,6 );
share->rec[i].space_length_bits=get_bits(&bit_buff,5 );
share->rec[i].huff_tree=share->decode_trees+(uint) get_bits(&bit_buff,
huff_tree_bits);
share->rec[i].unpack=get_unpack_function(share->rec+i);
DBUG_PRINT("info" , ("col: %2u type: %2u pack: %u slbits: %2u" ,
i, share->rec[i].base_type, share->rec[i].pack_type,
share->rec[i].space_length_bits));
}
skip_to_next_byte(&bit_buff);
/*
Construct the decoding tables from the file header . Keep track of
the used memory .
*/
decode_table=share->decode_tables;
for (i=0 ; i < trees ; i++)
if (read_huff_table(&bit_buff,share->decode_trees+i,&decode_table,
&intervall_buff,tmp_buff))
goto err3;
/* Reallocate the decoding tables to the used size. */
decode_table=(uint16*)
my_realloc(mi_key_memory_MYISAM_SHARE_decode_tables,
(uchar*) share->decode_tables,
(uint) ((uchar*) decode_table - (uchar*) share->decode_tables),
MYF(0 ));
/* Fix the table addresses in the tree heads. */
{
my_ptrdiff_t diff=PTR_BYTE_DIFF(decode_table,share->decode_tables);
share->decode_tables=decode_table;
for (i=0 ; i < trees ; i++)
share->decode_trees[i].table=ADD_TO_PTR(share->decode_trees[i].table,
diff, uint16*);
}
/* Fix record-ref-length for keys */
if (fix_keys)
{
for (i=0 ; i < share->base.keys ; i++)
{
MI_KEYDEF *keyinfo= &share->keyinfo[i];
keyinfo->keylength+= (uint16) diff_length;
keyinfo->minlength+= (uint16) diff_length;
keyinfo->maxlength+= (uint16) diff_length;
keyinfo->seg[keyinfo->key_alg == HA_KEY_ALG_FULLTEXT ?
FT_SEGS : keyinfo->keysegs].length= (uint16) rec_reflength;
}
if (share->ft2_keyinfo.seg)
{
MI_KEYDEF *ft2_keyinfo= &share->ft2_keyinfo;
ft2_keyinfo->keylength+= (uint16) diff_length;
ft2_keyinfo->minlength+= (uint16) diff_length;
ft2_keyinfo->maxlength+= (uint16) diff_length;
}
}
if (bit_buff.error || bit_buff.pos < bit_buff.end)
goto err3;
DBUG_RETURN(0 );
err3:
my_errno=HA_ERR_WRONG_IN_RECORD;
err2:
my_free(share->decode_tables);
err1:
my_free(share->decode_trees);
err0:
DBUG_RETURN(1 );
}
/*
Read a huff - code - table from datafile .
SYNOPSIS
read_huff_table ( )
bit_buff Bit buffer pointing at start of the
decoding table in the file header cache .
decode_tree Pointer to the decode tree head .
decode_table IN / OUT Address of a pointer to the next free space .
intervall_buff IN / OUT Address of a pointer to the next unused values .
tmp_buff Buffer for temporary extraction of a full
decoding table as read from bit_buff .
RETURN
0 OK .
1 Error .
*/
static uint read_huff_table(MI_BIT_BUFF *bit_buff, MI_DECODE_TREE *decode_tree,
uint16 **decode_table, uchar **intervall_buff,
uint16 *tmp_buff)
{
uint min_chr,elements,char_bits,offset_bits,size,intervall_length,table_bits,
next_free_offset;
uint16 *ptr,*end;
DBUG_ENTER("read_huff_table" );
if (!get_bits(bit_buff,1 ))
{
/* Byte value compression. */
min_chr=get_bits(bit_buff,8 );
elements=get_bits(bit_buff,9 );
char_bits=get_bits(bit_buff,5 );
offset_bits=get_bits(bit_buff,5 );
intervall_length=0 ;
ptr=tmp_buff;
DBUG_PRINT("info" , ("byte value compression" ));
DBUG_PRINT("info" , ("minimum byte value: %u" , min_chr));
DBUG_PRINT("info" , ("number of tree nodes: %u" , elements));
DBUG_PRINT("info" , ("bits for values: %u" , char_bits));
DBUG_PRINT("info" , ("bits for tree offsets: %u" , offset_bits));
if (elements > 256 )
{
DBUG_PRINT("error" , ("ERROR: illegal number of tree elements: %u" ,
elements));
DBUG_RETURN(1 );
}
}
else
{
/* Distinct column value compression. */
min_chr=0 ;
elements=get_bits(bit_buff,15 );
intervall_length=get_bits(bit_buff,16 );
char_bits=get_bits(bit_buff,5 );
offset_bits=get_bits(bit_buff,5 );
decode_tree->quick_table_bits=0 ;
ptr= *decode_table;
DBUG_PRINT("info" , ("distinct column value compression" ));
DBUG_PRINT("info" , ("number of tree nodes: %u" , elements));
DBUG_PRINT("info" , ("value buffer length: %u" , intervall_length));
DBUG_PRINT("info" , ("bits for value index: %u" , char_bits));
DBUG_PRINT("info" , ("bits for tree offsets: %u" , offset_bits));
}
size=elements*2 -2 ;
DBUG_PRINT("info" , ("tree size in uint16: %u" , size));
DBUG_PRINT("info" , ("tree size in bytes: %u" ,
size * (uint) sizeof (uint16)));
for (end=ptr+size ; ptr < end ; ptr++)
{
if (get_bit(bit_buff))
{
*ptr= (uint16) get_bits(bit_buff,offset_bits);
if ((ptr + *ptr >= end) || !*ptr)
{
DBUG_PRINT("error" , ("ERROR: illegal pointer in decode tree" ));
DBUG_RETURN(1 );
}
}
else
*ptr= (uint16) (IS_CHAR + (get_bits(bit_buff,char_bits) + min_chr));
}
skip_to_next_byte(bit_buff);
decode_tree->table= *decode_table;
decode_tree->intervalls= *intervall_buff;
if (! intervall_length)
{
/* Byte value compression. ptr started from tmp_buff. */
/* Find longest Huffman code from begin to end of tree in bits. */
table_bits= find_longest_bitstream(tmp_buff, ptr);
if (table_bits >= OFFSET_TABLE_SIZE)
DBUG_RETURN(1 );
if (table_bits > myisam_quick_table_bits)
table_bits=myisam_quick_table_bits;
DBUG_PRINT("info" , ("table bits: %u" , table_bits));
next_free_offset= (1 << table_bits);
make_quick_table(*decode_table,tmp_buff,&next_free_offset,0 ,table_bits,
table_bits);
(*decode_table)+= next_free_offset;
decode_tree->quick_table_bits=table_bits;
}
else
{
/* Distinct column value compression. ptr started from *decode_table */
(*decode_table)=end;
/*
get_bits ( ) moves some bytes to a cache buffer in advance . May need
to step back .
*/
bit_buff->pos-= bit_buff->bits/8 ;
/* Copy the distinct column values from the buffer. */
memcpy(*intervall_buff,bit_buff->pos,(size_t) intervall_length);
(*intervall_buff)+=intervall_length;
bit_buff->pos+=intervall_length;
bit_buff->bits=0 ;
}
DBUG_RETURN(0 );
}
/*
Make a quick_table for faster decoding .
SYNOPSIS
make_quick_table ( )
to_table Target quick_table and remaining decode table .
decode_table Source Huffman ( sub - ) tree within tmp_buff .
next_free_offset IN / OUT Next free offset from to_table .
Starts behind quick_table on the top - level .
value Huffman bits found so far .
bits Remaining bits to be collected .
max_bits Total number of bits to collect ( table_bits ) .
DESCRIPTION
The quick table is an array of 16 - bit values . There exists one value
for each possible code representable by max_bits ( table_bits ) bits .
In most cases table_bits is 9 . So there are 512 16 - bit values .
If the high - order bit ( 16 ) is set ( IS_CHAR ) then the array slot for
this value is a valid Huffman code for a resulting byte value .
The low - order 8 bits ( 1 . . 8 ) are the resulting byte value .
Bits 9 . . 14 are the length of the Huffman code for this byte value .
This means so many bits from the input stream were needed to
represent this byte value . The remaining bits belong to later
Huffman codes . This also means that for every Huffman code shorter
than table_bits there are multiple entires in the array , which
differ just in the unused bits .
If the high - order bit ( 16 ) is clear ( 0 ) then the remaining bits are
the position of the remaining Huffman decode tree segment behind the
quick table .
RETURN
void
*/
static void make_quick_table(uint16 *to_table, uint16 *decode_table,
uint *next_free_offset, uint value, uint bits,
uint max_bits)
{
DBUG_ENTER("make_quick_table" );
/*
When down the table to the requested maximum , copy the rest of the
Huffman table .
*/
if (!bits--)
{
/*
Remaining left Huffman tree segment starts behind quick table .
Remaining right Huffman tree segment starts behind left segment .
*/
to_table[value]= (uint16) *next_free_offset;
/*
Re - construct the remaining Huffman tree segment at
next_free_offset in to_table .
*/
*next_free_offset= copy_decode_table(to_table, *next_free_offset,
decode_table);
DBUG_VOID_RETURN;
}
/* Descent on the left side. Left side bits are clear (0). */
if (!(*decode_table & IS_CHAR))
{
/* Not a leaf. Follow the pointer. */
make_quick_table(to_table, decode_table + *decode_table,
next_free_offset, value, bits, max_bits);
}
else
{
/*
A leaf . A Huffman code is complete . Fill the quick_table
array for all possible bit strings starting with this Huffman
code .
*/
fill_quick_table(to_table + value, bits, max_bits, (uint) *decode_table);
}
/* Descent on the right side. Right side bits are set (1). */
decode_table++;
value|= (1 << bits);
if (!(*decode_table & IS_CHAR))
{
/* Not a leaf. Follow the pointer. */
make_quick_table(to_table, decode_table + *decode_table,
next_free_offset, value, bits, max_bits);
}
else
{
/*
A leaf . A Huffman code is complete . Fill the quick_table
array for all possible bit strings starting with this Huffman
code .
*/
fill_quick_table(to_table + value, bits, max_bits, (uint) *decode_table);
}
DBUG_VOID_RETURN;
}
/*
Fill quick_table for all possible values starting with this Huffman code .
SYNOPSIS
fill_quick_table ( )
table Target quick_table position .
bits Unused bits from max_bits .
max_bits Total number of bits to collect ( table_bits ) .
value The byte encoded by the found Huffman code .
DESCRIPTION
Fill the segment ( all slots ) of the quick_table array with the
resulting value for the found Huffman code . There are as many slots
as there are combinations representable by the unused bits .
In most cases we use 9 table bits . Assume a 3 - bit Huffman code . Then
there are 6 unused bits . Hence we fill 2 * * 6 = 64 slots with the
value .
RETURN
void
*/
static void fill_quick_table(uint16 *table, uint bits, uint max_bits,
uint value)
{
uint16 *end;
DBUG_ENTER("fill_quick_table" );
/*
Bits 1 . . 8 of value represent the decoded byte value .
Bits 9 . . 14 become the length of the Huffman code for this byte value .
Bit 16 flags a valid code ( IS_CHAR ) .
*/
value|= (max_bits - bits) << 8 | IS_CHAR;
for (end= table + ((my_ptrdiff_t) 1 << bits); table < end; table++)
{
*table= (uint16) value;
}
DBUG_VOID_RETURN;
}
/*
Reconstruct a decode subtree at the target position .
SYNOPSIS
copy_decode_table ( )
to_pos Target quick_table and remaining decode table .
offset Next free offset from to_pos .
decode_table Source Huffman subtree within tmp_buff .
NOTE
Pointers in the decode tree are relative to the pointers position .
RETURN
next free offset from to_pos .
*/
static uint copy_decode_table(uint16 *to_pos, uint offset,
uint16 *decode_table)
{
uint prev_offset= offset;
DBUG_ENTER("copy_decode_table" );
/* Descent on the left side. */
if (!(*decode_table & IS_CHAR))
{
/* Set a pointer to the next target node. */
to_pos[offset]=2 ;
/* Copy the left hand subtree there. */
offset=copy_decode_table(to_pos,offset+2 ,decode_table+ *decode_table);
}
else
{
/* Copy the byte value. */
to_pos[offset]= *decode_table;
/* Step behind this node. */
offset+=2 ;
}
/* Descent on the right side. */
decode_table++;
if (!(*decode_table & IS_CHAR))
{
/* Set a pointer to the next free target node. */
to_pos[prev_offset+1 ]=(uint16) (offset-prev_offset-1 );
/* Copy the right hand subtree to the entry of that node. */
offset=copy_decode_table(to_pos,offset,decode_table+ *decode_table);
}
else
{
/* Copy the byte value. */
to_pos[prev_offset+1 ]= *decode_table;
}
DBUG_RETURN(offset);
}
/*
Find the length of the longest Huffman code in this table in bits .
SYNOPSIS
find_longest_bitstream ( )
table Code ( sub - ) table start .
end End of code table .
IMPLEMENTATION
Recursively follow the branch ( es ) of the code pair on every level of
the tree until two byte values ( and no branch ) are found . Add one to
each level when returning back from each recursion stage .
' end ' is used for error checking only . A clean tree terminates
before reaching ' end ' . Hence the exact value of ' end ' is not too
important . However having it higher than necessary could lead to
misbehaviour should ' next ' jump into the dirty area .
RETURN
length Length of longest Huffman code in bits .
> = OFFSET_TABLE_SIZE Error , broken tree . It does not end before ' end ' .
*/
static uint find_longest_bitstream(uint16 *table, uint16 *end)
{
uint length= 1 ;
uint length2;
if (!(*table & IS_CHAR))
{
uint16 *next= table + *table;
if (next > end || next == table)
{
DBUG_PRINT("error" , ("ERROR: illegal pointer in decode tree" ));
return OFFSET_TABLE_SIZE;
}
length= find_longest_bitstream(next, end) + 1 ;
}
table++;
if (!(*table & IS_CHAR))
{
uint16 *next= table + *table;
if (next > end || next == table)
{
DBUG_PRINT("error" , ("ERROR: illegal pointer in decode tree" ));
return OFFSET_TABLE_SIZE;
}
length2= find_longest_bitstream(next, end) + 1 ;
length=MY_MAX(length,length2);
}
return length;
}
/*
Read record from datafile .
SYNOPSIS
_ mi_read_pack_record ( )
info A pointer to MI_INFO .
filepos File offset of the record .
buf RETURN The buffer to receive the record .
RETURN
0 on success
HA_ERR_WRONG_IN_RECORD or - 1 on error
*/
int _mi_read_pack_record(MI_INFO *info, my_off_t filepos, uchar *buf)
{
MI_BLOCK_INFO block_info;
File file;
DBUG_ENTER("mi_read_pack_record" );
if (filepos == HA_OFFSET_ERROR)
DBUG_RETURN(-1 ); /* _search() didn't find record */
file=info->dfile;
if (_mi_pack_get_block_info(info, &info->bit_buff, &block_info,
&info->rec_buff, file, filepos))
goto err;
if (mysql_file_read(file, (uchar*) info->rec_buff + block_info.offset,
block_info.rec_len - block_info.offset, MYF(MY_NABP)))
goto panic;
info->update|= HA_STATE_AKTIV;
info->rec_buff[block_info.rec_len]= 0 ; /* Keep valgrind happy */
DBUG_RETURN(_mi_pack_rec_unpack(info, &info->bit_buff, buf,
info->rec_buff, block_info.rec_len));
panic:
my_errno=HA_ERR_WRONG_IN_RECORD;
err:
DBUG_RETURN(-1 );
}
int _mi_pack_rec_unpack(register MI_INFO *info, MI_BIT_BUFF *bit_buff,
register uchar *to, uchar *from, ulong reclength)
{
uchar *end_field;
reg3 MI_COLUMNDEF *end;
MI_COLUMNDEF *current_field;
MYISAM_SHARE *share=info->s;
DBUG_ENTER("_mi_pack_rec_unpack" );
init_bit_buffer(bit_buff, (uchar*) from, reclength);
for (current_field=share->rec, end=current_field+share->base.fields ;
current_field < end ;
current_field++,to=end_field)
{
end_field=to+current_field->length;
(*current_field->unpack)(current_field, bit_buff, (uchar*) to,
(uchar*) end_field);
}
if (!bit_buff->error &&
bit_buff->pos - bit_buff->bits / 8 == bit_buff->end)
DBUG_RETURN(0 );
info->update&= ~HA_STATE_AKTIV;
DBUG_RETURN(my_errno=HA_ERR_WRONG_IN_RECORD);
} /* _mi_pack_rec_unpack */
/* Return function to unpack field */
static void (*get_unpack_function(MI_COLUMNDEF *rec))
(MI_COLUMNDEF *, MI_BIT_BUFF *, uchar *, uchar *)
{
switch (rec->base_type) {
case FIELD_SKIP_ZERO:
if (rec->pack_type & PACK_TYPE_ZERO_FILL)
return &uf_zerofill_skip_zero;
return &uf_skip_zero;
case FIELD_NORMAL:
if (rec->pack_type & PACK_TYPE_SPACE_FIELDS)
return &uf_space_normal;
if (rec->pack_type & PACK_TYPE_ZERO_FILL)
return &uf_zerofill_normal;
return &decode_bytes;
case FIELD_SKIP_ENDSPACE:
if (rec->pack_type & PACK_TYPE_SPACE_FIELDS)
{
if (rec->pack_type & PACK_TYPE_SELECTED)
return &uf_space_endspace_selected;
return &uf_space_endspace;
}
if (rec->pack_type & PACK_TYPE_SELECTED)
return &uf_endspace_selected;
return &uf_endspace;
case FIELD_SKIP_PRESPACE:
if (rec->pack_type & PACK_TYPE_SPACE_FIELDS)
{
if (rec->pack_type & PACK_TYPE_SELECTED)
return &uf_space_prespace_selected;
return &uf_space_prespace;
}
if (rec->pack_type & PACK_TYPE_SELECTED)
return &uf_prespace_selected;
return &uf_prespace;
case FIELD_CONSTANT:
return &uf_constant;
case FIELD_INTERVALL:
return &uf_intervall;
case FIELD_ZERO:
case FIELD_CHECK:
return &uf_zero;
case FIELD_BLOB:
return &uf_blob;
case FIELD_VARCHAR:
if (rec->length <= 256 ) /* 255 + 1 byte length */
return &uf_varchar1;
return &uf_varchar2;
case FIELD_LAST:
default :
return 0 ; /* This should never happen */
}
}
/* The different functions to unpack a field */
static void uf_zerofill_skip_zero(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end)
{
if (get_bit(bit_buff))
bzero((char *) to,(uint) (end-to));
else
{
end-=rec->space_length_bits;
decode_bytes(rec,bit_buff,to,end);
bzero((char *) end,rec->space_length_bits);
}
}
static void uf_skip_zero(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
uchar *end)
{
if (get_bit(bit_buff))
bzero((char *) to,(uint) (end-to));
else
decode_bytes(rec,bit_buff,to,end);
}
static void uf_space_normal(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
uchar *end)
{
if (get_bit(bit_buff))
bfill((uchar*) to,(end-to),' ' );
else
decode_bytes(rec,bit_buff,to,end);
}
static void uf_space_endspace_selected(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end)
{
uint spaces;
if (get_bit(bit_buff))
bfill((uchar*) to,(end-to),' ' );
else
{
if (get_bit(bit_buff))
{
if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
{
bit_buff->error=1 ;
return ;
}
if (to+spaces != end)
decode_bytes(rec,bit_buff,to,end-spaces);
bfill((uchar*) end-spaces,spaces,' ' );
}
else
decode_bytes(rec,bit_buff,to,end);
}
}
static void uf_endspace_selected(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end)
{
uint spaces;
if (get_bit(bit_buff))
{
if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
{
bit_buff->error=1 ;
return ;
}
if (to+spaces != end)
decode_bytes(rec,bit_buff,to,end-spaces);
bfill((uchar*) end-spaces,spaces,' ' );
}
else
decode_bytes(rec,bit_buff,to,end);
}
static void uf_space_endspace(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
uchar *end)
{
uint spaces;
if (get_bit(bit_buff))
bfill((uchar*) to,(end-to),' ' );
else
{
if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
{
bit_buff->error=1 ;
return ;
}
if (to+spaces != end)
decode_bytes(rec,bit_buff,to,end-spaces);
bfill((uchar*) end-spaces,spaces,' ' );
}
}
static void uf_endspace(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
uchar *end)
{
uint spaces;
if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
{
bit_buff->error=1 ;
return ;
}
if (to+spaces != end)
decode_bytes(rec,bit_buff,to,end-spaces);
bfill((uchar*) end-spaces,spaces,' ' );
}
static void uf_space_prespace_selected(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end)
{
uint spaces;
if (get_bit(bit_buff))
bfill((uchar*) to,(end-to),' ' );
else
{
if (get_bit(bit_buff))
{
if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
{
bit_buff->error=1 ;
return ;
}
bfill((uchar*) to,spaces,' ' );
if (to+spaces != end)
decode_bytes(rec,bit_buff,to+spaces,end);
}
else
decode_bytes(rec,bit_buff,to,end);
}
}
static void uf_prespace_selected(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end)
{
uint spaces;
if (get_bit(bit_buff))
{
if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
{
bit_buff->error=1 ;
return ;
}
bfill((uchar*) to,spaces,' ' );
if (to+spaces != end)
decode_bytes(rec,bit_buff,to+spaces,end);
}
else
decode_bytes(rec,bit_buff,to,end);
}
static void uf_space_prespace(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
uchar *end)
{
uint spaces;
if (get_bit(bit_buff))
bfill((uchar*) to,(end-to),' ' );
else
{
if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
{
bit_buff->error=1 ;
return ;
}
bfill((uchar*) to,spaces,' ' );
if (to+spaces != end)
decode_bytes(rec,bit_buff,to+spaces,end);
}
}
static void uf_prespace(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
uchar *end)
{
uint spaces;
if ((spaces=get_bits(bit_buff,rec->space_length_bits))+to > end)
{
bit_buff->error=1 ;
return ;
}
bfill((uchar*) to,spaces,' ' );
if (to+spaces != end)
decode_bytes(rec,bit_buff,to+spaces,end);
}
static void uf_zerofill_normal(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
uchar *end)
{
end-=rec->space_length_bits;
decode_bytes(rec,bit_buff,(uchar*) to,end);
bzero((char *) end,rec->space_length_bits);
}
static void uf_constant(MI_COLUMNDEF *rec,
MI_BIT_BUFF *bit_buff __attribute__((unused)),
uchar *to,
uchar *end)
{
memcpy(to,rec->huff_tree->intervalls,(size_t) (end-to));
}
static void uf_intervall(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
uchar *end)
{
reg1 uint field_length=(uint) (end-to);
memcpy(to,rec->huff_tree->intervalls+field_length*decode_pos(bit_buff,
rec->huff_tree),
(size_t) field_length);
}
/*ARGSUSED*/
static void uf_zero(MI_COLUMNDEF *rec __attribute__((unused)),
MI_BIT_BUFF *bit_buff __attribute__((unused)),
uchar *to, uchar *end)
{
bzero((char *) to,(uint) (end-to));
}
static void uf_blob(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end)
{
if (get_bit(bit_buff))
bzero((uchar*) to,(end-to));
else
{
ulong length=get_bits(bit_buff,rec->space_length_bits);
uint pack_length=(uint) (end-to)-portable_sizeof_char_ptr;
if (bit_buff->blob_pos+length > bit_buff->blob_end)
{
bit_buff->error=1 ;
bzero((uchar*) to,(end-to));
return ;
}
decode_bytes(rec,bit_buff,bit_buff->blob_pos,bit_buff->blob_pos+length);
_mi_store_blob_length((uchar*) to,pack_length,length);
memcpy(to+pack_length, &bit_buff->blob_pos, sizeof (char *));
bit_buff->blob_pos+=length;
}
}
static void uf_varchar1(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end __attribute__((unused)))
{
if (get_bit(bit_buff))
to[0 ]= 0 ; /* Zero lengths */
else
{
ulong length=get_bits(bit_buff,rec->space_length_bits);
*to= (uchar) length;
decode_bytes(rec,bit_buff,to+1 ,to+1 +length);
}
}
static void uf_varchar2(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff,
uchar *to, uchar *end __attribute__((unused)))
{
if (get_bit(bit_buff))
to[0 ]=to[1 ]=0 ; /* Zero lengths */
else
{
ulong length=get_bits(bit_buff,rec->space_length_bits);
int2store(to,length);
decode_bytes(rec,bit_buff,to+2 ,to+2 +length);
}
}
/* Functions to decode of buffer of bits */
#if BITS_SAVED == 64
static void decode_bytes(MI_COLUMNDEF *rec,MI_BIT_BUFF *bit_buff,uchar *to,
uchar *end)
{
reg1 uint bits,low_byte;
reg3 uint16 *pos;
reg4 uint table_bits,table_and;
MI_DECODE_TREE *decode_tree;
decode_tree=rec->decode_tree;
bits=bit_buff->bits; /* Save in reg for quicker access */
table_bits=decode_tree->quick_table_bits;
table_and= (1 << table_bits)-1 ;
do
{
if (bits <= 32 )
{
if (bit_buff->pos > bit_buff->end+4 )
{
bit_buff->error=1 ;
return ; /* Can't be right */
}
bit_buff->current_byte= (bit_buff->current_byte << 32 ) |
((((uint) bit_buff->pos[3 ])) |
(((uint) bit_buff->pos[2 ]) << 8 ) |
(((uint) bit_buff->pos[1 ]) << 16 ) |
(((uint) bit_buff->pos[0 ]) << 24 ));
bit_buff->pos+=4 ;
bits+=32 ;
}
/*
First use info in quick_table .
The quick table is an array of 16 - bit values . There exists one
value for each possible code representable by table_bits bits .
In most cases table_bits is 9 . So there are 512 16 - bit values .
If the high - order bit ( 16 ) is set ( IS_CHAR ) then the array slot
for this value is a valid Huffman code for a resulting byte value .
The low - order 8 bits ( 1 . . 8 ) are the resulting byte value .
Bits 9 . . 14 are the length of the Huffman code for this byte value .
This means so many bits from the input stream were needed to
represent this byte value . The remaining bits belong to later
Huffman codes . This also means that for every Huffman code shorter
than table_bits there are multiple entires in the array , which
differ just in the unused bits .
If the high - order bit ( 16 ) is clear ( 0 ) then the remaining bits are
the position of the remaining Huffman decode tree segment behind the
quick table .
*/
low_byte=(uint) (bit_buff->current_byte >> (bits - table_bits)) & table_and;
low_byte=decode_tree->table[low_byte];
if (low_byte & IS_CHAR)
{
/*
All Huffman codes of less or equal table_bits length are in the
quick table . This is one of them .
*/
*to++ = (low_byte & 255 ); /* Found char in quick table */
bits-= ((low_byte >> 8 ) & 31 ); /* Remove bits used */
}
else
{ /* Map through rest of decode-table */
/* This means that the Huffman code must be longer than table_bits. */
pos=decode_tree->table+low_byte;
bits-=table_bits;
/* NOTE: decode_bytes_test_bit() is a macro which contains a break !!! */
for (;;)
{
low_byte=(uint) (bit_buff->current_byte >> (bits-8 ));
decode_bytes_test_bit(0 );
decode_bytes_test_bit(1 );
decode_bytes_test_bit(2 );
decode_bytes_test_bit(3 );
decode_bytes_test_bit(4 );
decode_bytes_test_bit(5 );
decode_bytes_test_bit(6 );
decode_bytes_test_bit(7 );
bits-=8 ;
}
*to++ = *pos;
}
} while (to != end);
bit_buff->bits=bits;
return ;
}
#else
static void decode_bytes(MI_COLUMNDEF *rec, MI_BIT_BUFF *bit_buff, uchar *to,
uchar *end)
{
reg1 uint bits,low_byte;
reg3 uint16 *pos;
reg4 uint table_bits,table_and;
MI_DECODE_TREE *decode_tree;
decode_tree=rec->huff_tree;
bits=bit_buff->bits; /* Save in reg for quicker access */
table_bits=decode_tree->quick_table_bits;
table_and= (1 << table_bits)-1 ;
do
{
if (bits < table_bits)
{
if (bit_buff->pos > bit_buff->end+1 )
{
bit_buff->error=1 ;
return ; /* Can't be right */
}
#if BITS_SAVED == 32
bit_buff->current_byte= (bit_buff->current_byte << 24 ) |
(((uint) ((uchar) bit_buff->pos[2 ]))) |
(((uint) ((uchar) bit_buff->pos[1 ])) << 8 ) |
(((uint) ((uchar) bit_buff->pos[0 ])) << 16 );
bit_buff->pos+=3 ;
bits+=24 ;
#else
if (bits) /* We must have at leasts 9 bits */
{
bit_buff->current_byte= (bit_buff->current_byte << 8 ) |
(uint) ((uchar) bit_buff->pos[0 ]);
bit_buff->pos++;
bits+=8 ;
}
else
{
bit_buff->current_byte= ((uint) ((uchar) bit_buff->pos[0 ]) << 8 ) |
((uint) ((uchar) bit_buff->pos[1 ]));
bit_buff->pos+=2 ;
bits+=16 ;
}
#endif
}
/* First use info in quick_table */
low_byte=(bit_buff->current_byte >> (bits - table_bits)) & table_and;
low_byte=decode_tree->table[low_byte];
if (low_byte & IS_CHAR)
{
*to++ = (low_byte & 255 ); /* Found char in quick table */
bits-= ((low_byte >> 8 ) & 31 ); /* Remove bits used */
}
else
{ /* Map through rest of decode-table */
pos=decode_tree->table+low_byte;
bits-=table_bits;
for (;;)
{
if (bits < 8 )
{ /* We don't need to check end */
#if BITS_SAVED == 32
bit_buff->current_byte= (bit_buff->current_byte << 24 ) |
(((uint) ((uchar) bit_buff->pos[2 ]))) |
(((uint) ((uchar) bit_buff->pos[1 ])) << 8 ) |
(((uint) ((uchar) bit_buff->pos[0 ])) << 16 );
bit_buff->pos+=3 ;
bits+=24 ;
#else
bit_buff->current_byte= (bit_buff->current_byte << 8 ) |
(uint) ((uchar) bit_buff->pos[0 ]);
bit_buff->pos+=1 ;
bits+=8 ;
#endif
}
low_byte=(uint) (bit_buff->current_byte >> (bits-8 ));
decode_bytes_test_bit(0 );
decode_bytes_test_bit(1 );
decode_bytes_test_bit(2 );
decode_bytes_test_bit(3 );
decode_bytes_test_bit(4 );
decode_bytes_test_bit(5 );
decode_bytes_test_bit(6 );
decode_bytes_test_bit(7 );
bits-=8 ;
}
*to++ = (uchar) *pos;
}
} while (to != end);
bit_buff->bits=bits;
return ;
}
#endif /* BIT_SAVED == 64 */
static uint decode_pos(MI_BIT_BUFF *bit_buff, MI_DECODE_TREE *decode_tree)
{
uint16 *pos=decode_tree->table;
for (;;)
{
if (get_bit(bit_buff))
pos++;
if (*pos & IS_CHAR)
return (uint) (*pos & ~IS_CHAR);
pos+= *pos;
}
}
int _mi_read_rnd_pack_record(MI_INFO *info, uchar *buf,
register my_off_t filepos,
my_bool skip_deleted_blocks)
{
uint b_type;
MI_BLOCK_INFO block_info;
MYISAM_SHARE *share=info->s;
DBUG_ENTER("_mi_read_rnd_pack_record" );
if (filepos >= info->state->data_file_length)
{
my_errno= HA_ERR_END_OF_FILE;
goto err;
}
if (info->opt_flag & READ_CACHE_USED)
{
if (_mi_read_cache(&info->rec_cache, (uchar*) block_info.header,
filepos, share->pack .ref_length,
skip_deleted_blocks ? READING_NEXT : 0 ))
goto err;
b_type=_mi_pack_get_block_info(info, &info->bit_buff, &block_info,
&info->rec_buff, -1 , filepos);
}
else
b_type=_mi_pack_get_block_info(info, &info->bit_buff, &block_info,
&info->rec_buff, info->dfile, filepos);
if (b_type)
goto err; /* Error code is already set */
#ifndef DBUG_OFF
if (block_info.rec_len > share->max_pack_length)
{
my_errno=HA_ERR_WRONG_IN_RECORD;
goto err;
}
#endif
if (info->opt_flag & READ_CACHE_USED)
{
if (_mi_read_cache(&info->rec_cache, (uchar*) info->rec_buff,
block_info.filepos, block_info.rec_len,
skip_deleted_blocks ? READING_NEXT : 0 ))
goto err;
}
else
{
if (mysql_file_read(info->dfile,
(uchar*) info->rec_buff + block_info.offset,
block_info.rec_len-block_info.offset, MYF(MY_NABP)))
goto err;
}
info->packed_length=block_info.rec_len;
info->lastpos=filepos;
info->nextpos=block_info.filepos+block_info.rec_len;
info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED;
info->rec_buff[block_info.rec_len]= 0 ; /* Keep valgrind happy */
DBUG_RETURN(_mi_pack_rec_unpack(info, &info->bit_buff, buf,
info->rec_buff, block_info.rec_len));
err:
DBUG_RETURN(my_errno);
}
/* Read and process header from a huff-record-file */
uint _mi_pack_get_block_info(MI_INFO *myisam, MI_BIT_BUFF *bit_buff,
MI_BLOCK_INFO *info, uchar **rec_buff_p,
File file, my_off_t filepos)
{
uchar *header=info->header;
uint head_length, UNINIT_VAR(ref_length);
if (file >= 0 )
{
ref_length=myisam->s->pack .ref_length;
/*
We can ' t use mysql_file_pread ( ) here because mi_read_rnd_pack_record
assumes position is ok
*/
mysql_file_seek(file, filepos, MY_SEEK_SET, MYF(0 ));
if (mysql_file_read(file, header, ref_length, MYF(MY_NABP)))
return BLOCK_FATAL_ERROR;
DBUG_DUMP("header" ,(uchar*) header,ref_length);
}
head_length= read_pack_length((uint) myisam->s->pack .version, header,
&info->rec_len);
if (myisam->s->base.blobs)
{
head_length+= read_pack_length((uint) myisam->s->pack .version,
header + head_length, &info->blob_len);
/*
Ensure that the record buffer is big enough for the compressed
record plus all expanded blobs . [ We do not have an extra buffer
for the resulting blobs . Sigh . ]
*/
if (!(mi_alloc_rec_buff(myisam,info->rec_len + info->blob_len,
rec_buff_p)))
return BLOCK_FATAL_ERROR; /* not enough memory */
bit_buff->blob_pos= (uchar*) *rec_buff_p + info->rec_len;
bit_buff->blob_end= bit_buff->blob_pos + info->blob_len;
myisam->blob_length=info->blob_len;
}
info->filepos=filepos+head_length;
if (file > 0 )
{
info->offset=MY_MIN(info->rec_len, ref_length - head_length);
memcpy(*rec_buff_p, header + head_length, info->offset);
}
return 0 ;
}
/*
Routines for bit buffer
Note : buffer must be 6 byte bigger than longest row
*/
static void init_bit_buffer(MI_BIT_BUFF *bit_buff, uchar *buffer, uint length)
{
bit_buff->pos=buffer;
bit_buff->end=buffer+length;
bit_buff->bits=bit_buff->error=0 ;
bit_buff->current_byte=0 ; /* Avoid valgrind errors */
}
static uint fill_and_get_bits(MI_BIT_BUFF *bit_buff, uint count)
{
uint tmp;
count-=bit_buff->bits;
tmp=(bit_buff->current_byte & mask[bit_buff->bits]) << count;
fill_buffer(bit_buff);
bit_buff->bits=BITS_SAVED - count;
return tmp+(bit_buff->current_byte >> (BITS_SAVED - count));
}
/* Fill in empty bit_buff->current_byte from buffer */
/* Sets bit_buff->error if buffer is exhausted */
static void fill_buffer(MI_BIT_BUFF *bit_buff)
{
if (bit_buff->pos >= bit_buff->end)
{
bit_buff->error= 1 ;
bit_buff->current_byte=0 ;
return ;
}
#if BITS_SAVED == 64
bit_buff->current_byte= ((((uint) ((uchar) bit_buff->pos[7 ]))) |
(((uint) ((uchar) bit_buff->pos[6 ])) << 8 ) |
(((uint) ((uchar) bit_buff->pos[5 ])) << 16 ) |
(((uint) ((uchar) bit_buff->pos[4 ])) << 24 ) |
((ulonglong)
((((uint) ((uchar) bit_buff->pos[3 ]))) |
(((uint) ((uchar) bit_buff->pos[2 ])) << 8 ) |
(((uint) ((uchar) bit_buff->pos[1 ])) << 16 ) |
(((uint) ((uchar) bit_buff->pos[0 ])) << 24 )) << 32 ));
bit_buff->pos+=8 ;
#else
#if BITS_SAVED == 32
bit_buff->current_byte= (((uint) ((uchar) bit_buff->pos[3 ])) |
(((uint) ((uchar) bit_buff->pos[2 ])) << 8 ) |
(((uint) ((uchar) bit_buff->pos[1 ])) << 16 ) |
(((uint) ((uchar) bit_buff->pos[0 ])) << 24 ));
bit_buff->pos+=4 ;
#else
bit_buff->current_byte= (uint) (((uint) ((uchar) bit_buff->pos[1 ])) |
(((uint) ((uchar) bit_buff->pos[0 ])) << 8 ));
bit_buff->pos+=2 ;
#endif
#endif
}
/* Get number of bits neaded to represent value */
static uint max_bit(register uint value)
{
reg2 uint power=1 ;
while ((value>>=1 ))
power++;
return (power);
}
/*****************************************************************************
Some redefined functions to handle files when we are using memmap
*****************************************************************************/
#ifdef HAVE_MMAP
static int _mi_read_mempack_record(MI_INFO *info,my_off_t filepos,uchar *buf);
static int _mi_read_rnd_mempack_record(MI_INFO*, uchar *,my_off_t, my_bool);
my_bool _mi_memmap_file(MI_INFO *info)
{
MYISAM_SHARE *share=info->s;
my_bool eom;
DBUG_ENTER("mi_memmap_file" );
if (!info->s->file_map)
{
my_off_t data_file_length= share->state.state.data_file_length;
if (myisam_mmap_size != SIZE_T_MAX)
{
mysql_mutex_lock(&THR_LOCK_myisam_mmap);
eom= data_file_length > myisam_mmap_size - myisam_mmap_used - MEMMAP_EXTRA_MARGIN;
if (!eom)
myisam_mmap_used+= data_file_length + MEMMAP_EXTRA_MARGIN;
mysql_mutex_unlock(&THR_LOCK_myisam_mmap);
}
else
eom= data_file_length > myisam_mmap_size - MEMMAP_EXTRA_MARGIN;
if (eom)
{
DBUG_PRINT("warning" , ("File is too large for mmap" ));
DBUG_RETURN(0 );
}
if (mysql_file_seek(info->dfile, 0 L, MY_SEEK_END, MYF(0 )) <
share->state.state.data_file_length+MEMMAP_EXTRA_MARGIN)
{
DBUG_PRINT("warning" ,("File isn't extended for memmap" ));
if (myisam_mmap_size != SIZE_T_MAX)
{
mysql_mutex_lock(&THR_LOCK_myisam_mmap);
myisam_mmap_used-= data_file_length + MEMMAP_EXTRA_MARGIN;
mysql_mutex_unlock(&THR_LOCK_myisam_mmap);
}
DBUG_RETURN(0 );
}
if (mi_dynmap_file(info,
share->state.state.data_file_length +
MEMMAP_EXTRA_MARGIN))
{
if (myisam_mmap_size != SIZE_T_MAX)
{
mysql_mutex_lock(&THR_LOCK_myisam_mmap);
myisam_mmap_used-= data_file_length + MEMMAP_EXTRA_MARGIN;
mysql_mutex_unlock(&THR_LOCK_myisam_mmap);
}
DBUG_RETURN(0 );
}
}
info->opt_flag|= MEMMAP_USED;
info->read_record= share->read_record= _mi_read_mempack_record;
share->read_rnd= _mi_read_rnd_mempack_record;
DBUG_RETURN(1 );
}
void _mi_unmap_file(MI_INFO *info)
{
DBUG_ASSERT(info->s->options & HA_OPTION_COMPRESS_RECORD);
(void ) my_munmap((char *) info->s->file_map, info->s->mmaped_length);
if (myisam_mmap_size != SIZE_T_MAX)
{
mysql_mutex_lock(&THR_LOCK_myisam_mmap);
myisam_mmap_used-= info->s->mmaped_length;
mysql_mutex_unlock(&THR_LOCK_myisam_mmap);
}
}
static uchar *_mi_mempack_get_block_info(MI_INFO *myisam,
MI_BIT_BUFF *bit_buff,
MI_BLOCK_INFO *info,
uchar **rec_buff_p,
uchar *header)
{
header+= read_pack_length((uint) myisam->s->pack .version, header,
&info->rec_len);
if (myisam->s->base.blobs)
{
header+= read_pack_length((uint) myisam->s->pack .version, header,
&info->blob_len);
/* mi_alloc_rec_buff sets my_errno on error */
if (!(mi_alloc_rec_buff(myisam, info->blob_len ,
rec_buff_p)))
return 0 ; /* not enough memory */
bit_buff->blob_pos= (uchar*) *rec_buff_p;
bit_buff->blob_end= (uchar*) *rec_buff_p + info->blob_len;
}
return header;
}
static int _mi_read_mempack_record(MI_INFO *info, my_off_t filepos, uchar *buf)
{
MI_BLOCK_INFO block_info;
MYISAM_SHARE *share=info->s;
uchar *pos;
DBUG_ENTER("mi_read_mempack_record" );
if (filepos == HA_OFFSET_ERROR)
DBUG_RETURN(-1 ); /* _search() didn't find record */
if (!(pos= (uchar*) _mi_mempack_get_block_info(info, &info->bit_buff,
&block_info, &info->rec_buff,
(uchar*) share->file_map+
filepos)))
DBUG_RETURN(-1 );
/* No need to end-zero pos here for valgrind as data is memory mapped */
DBUG_RETURN(_mi_pack_rec_unpack(info, &info->bit_buff, buf,
pos, block_info.rec_len));
}
/*ARGSUSED*/
static int _mi_read_rnd_mempack_record(MI_INFO *info, uchar *buf,
register my_off_t filepos,
my_bool skip_deleted_blocks
__attribute__((unused)))
{
MI_BLOCK_INFO block_info;
MYISAM_SHARE *share=info->s;
uchar *pos,*start;
DBUG_ENTER("_mi_read_rnd_mempack_record" );
if (filepos >= share->state.state.data_file_length)
{
my_errno=HA_ERR_END_OF_FILE;
goto err;
}
if (!(pos= (uchar*) _mi_mempack_get_block_info(info, &info->bit_buff,
&block_info, &info->rec_buff,
(uchar*)
(start=share->file_map+
filepos))))
goto err;
#ifndef DBUG_OFF
if (block_info.rec_len > info->s->max_pack_length)
{
my_errno=HA_ERR_WRONG_IN_RECORD;
goto err;
}
#endif
info->packed_length=block_info.rec_len;
info->lastpos=filepos;
info->nextpos=filepos+(uint) (pos-start)+block_info.rec_len;
info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED;
DBUG_RETURN (_mi_pack_rec_unpack(info, &info->bit_buff, buf,
pos, block_info.rec_len));
err:
DBUG_RETURN(my_errno);
}
#endif /* HAVE_MMAP */
/* Save length of row */
uint save_pack_length(uint version, uchar *block_buff, ulong length)
{
if (length < 254 )
{
*(uchar*) block_buff= (uchar) length;
return 1 ;
}
if (length <= 65535 )
{
*(uchar*) block_buff=254 ;
int2store(block_buff+1 ,(uint) length);
return 3 ;
}
*(uchar*) block_buff=255 ;
if (version == 1 ) /* old format */
{
DBUG_ASSERT(length <= 0 xFFFFFF);
int3store(block_buff + 1 , (ulong) length);
return 4 ;
}
else
{
int4store(block_buff + 1 , (ulong) length);
return 5 ;
}
}
static uint read_pack_length(uint version, const uchar *buf, ulong *length)
{
if (buf[0 ] < 254 )
{
*length= buf[0 ];
return 1 ;
}
else if (buf[0 ] == 254 )
{
*length= uint2korr(buf + 1 );
return 3 ;
}
if (version == 1 ) /* old format */
{
*length= uint3korr(buf + 1 );
return 4 ;
}
else
{
*length= uint4korr(buf + 1 );
return 5 ;
}
}
uint calc_pack_length(uint version, ulong length)
{
return (length < 254 ) ? 1 : (length < 65536 ) ? 3 : (version == 1 ) ? 4 : 5 ;
}
Messung V0.5 in Prozent C=95 H=85 G=90
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland