~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2000-2006 MySQL AB
2
3
   This program is free software; you can redistribute it and/or modify
4
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 of the License.
6
7
   This program is distributed in the hope that it will be useful,
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
   GNU General Public License for more details.
11
12
   You should have received a copy of the GNU General Public License
13
   along with this program; if not, write to the Free Software
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
16
17
/**
18
  @file
19
20
  @brief
21
  Functions for easy reading of records, possible through a cache
22
*/
243.1.17 by Jay Pipes
FINAL PHASE removal of mysql_priv.h (Bye, bye my friend.)
23
#include <drizzled/server_includes.h>
550 by Monty Taylor
Moved error.h into just the files that need it.
24
#include <drizzled/error.h>
584.1.15 by Monty Taylor
The mega-patch from hell. Renamed sql_class to session (since that's what it is) and removed it and field and table from common_includes.
25
#include <drizzled/table.h>
26
#include <drizzled/session.h>
1 by brian
clean slate
27
28
static int rr_quick(READ_RECORD *info);
29
int rr_sequential(READ_RECORD *info);
30
static int rr_from_tempfile(READ_RECORD *info);
31
static int rr_unpack_from_tempfile(READ_RECORD *info);
32
static int rr_unpack_from_buffer(READ_RECORD *info);
33
static int rr_from_pointers(READ_RECORD *info);
34
static int rr_from_cache(READ_RECORD *info);
520.1.22 by Brian Aker
Second pass of thd cleanup
35
static int init_rr_cache(Session *session, READ_RECORD *info);
481 by Brian Aker
Remove all of uchar.
36
static int rr_cmp(unsigned char *a,unsigned char *b);
1 by brian
clean slate
37
static int rr_index_first(READ_RECORD *info);
38
static int rr_index(READ_RECORD *info);
39
40
41
/**
42
  Initialize READ_RECORD structure to perform full index scan (in forward
43
  direction) using read_record.read_record() interface.
44
45
    This function has been added at late stage and is used only by
46
    UPDATE/DELETE. Other statements perform index scans using
47
    join_read_first/next functions.
48
49
  @param info         READ_RECORD structure to initialize.
520.1.22 by Brian Aker
Second pass of thd cleanup
50
  @param session          Thread handle
1 by brian
clean slate
51
  @param table        Table to be accessed
52
  @param print_error  If true, call table->file->print_error() if an error
53
                      occurs (except for end-of-records error)
54
  @param idx          index to scan
55
*/
56
575.1.2 by Monty Taylor
Changed a bunch of __attribute__((unused)) to removing the parameter name instead.
57
void init_read_record_idx(READ_RECORD *info, Session *, Table *table,
482 by Brian Aker
Remove uint.
58
                          bool print_error, uint32_t idx)
1 by brian
clean slate
59
{
60
  empty_record(table);
212.6.6 by Mats Kindahl
Removing redundant use of casts in drizzled/ for memcmp(), memcpy(), memset(), and memmove().
61
  memset(info, 0, sizeof(*info));
1 by brian
clean slate
62
  info->table= table;
63
  info->file=  table->file;
64
  info->record= table->record[0];
65
  info->print_error= print_error;
66
67
  table->status=0;			/* And it's always found */
68
  if (!table->file->inited)
69
    table->file->ha_index_init(idx, 1);
70
  /* read_record will be changed to rr_index in rr_index_first */
71
  info->read_record= rr_index_first;
72
}
73
74
75
/*
76
  init_read_record is used to scan by using a number of different methods.
77
  Which method to use is set-up in this call so that later calls to
78
  the info->read_record will call the appropriate method using a function
79
  pointer.
80
81
  There are five methods that relate completely to the sort function
82
  filesort. The result of a filesort is retrieved using read_record
83
  calls. The other two methods are used for normal table access.
84
85
  The filesort will produce references to the records sorted, these
86
  references can be stored in memory or in a temporary file.
87
88
  The temporary file is normally used when the references doesn't fit into
89
  a properly sized memory buffer. For most small queries the references
90
  are stored in the memory buffer.
91
92
  The temporary file is also used when performing an update where a key is
93
  modified.
94
95
  Methods used when ref's are in memory (using rr_from_pointers):
96
    rr_unpack_from_buffer:
97
    ----------------------
98
      This method is used when table->sort.addon_field is allocated.
99
      This is allocated for most SELECT queries not involving any BLOB's.
100
      In this case the records are fetched from a memory buffer.
101
    rr_from_pointers:
102
    -----------------
103
      Used when the above is not true, UPDATE, DELETE and so forth and
104
      SELECT's involving BLOB's. It is also used when the addon_field
105
      buffer is not allocated due to that its size was bigger than the
106
      session variable max_length_for_sort_data.
107
      In this case the record data is fetched from the handler using the
108
      saved reference using the rnd_pos handler call.
109
110
  Methods used when ref's are in a temporary file (using rr_from_tempfile)
111
    rr_unpack_from_tempfile:
112
    ------------------------
113
      Same as rr_unpack_from_buffer except that references are fetched from
114
      temporary file. Should obviously not really happen other than in
115
      strange configurations.
116
117
    rr_from_tempfile:
118
    -----------------
119
      Same as rr_from_pointers except that references are fetched from
120
      temporary file instead of from 
121
    rr_from_cache:
122
    --------------
123
      This is a special variant of rr_from_tempfile that can be used for
124
      handlers that is not using the HA_FAST_KEY_READ table flag. Instead
125
      of reading the references one by one from the temporary file it reads
126
      a set of them, sorts them and reads all of them into a buffer which
127
      is then used for a number of subsequent calls to rr_from_cache.
128
      It is only used for SELECT queries and a number of other conditions
129
      on table size.
130
131
  All other accesses use either index access methods (rr_quick) or a full
132
  table scan (rr_sequential).
133
  rr_quick:
134
  ---------
135
    rr_quick uses one of the QUICK_SELECT classes in opt_range.cc to
136
    perform an index scan. There are loads of functionality hidden
137
    in these quick classes. It handles all index scans of various kinds.
138
  rr_sequential:
139
  --------------
140
    This is the most basic access method of a table using rnd_init,
141
    rnd_next and rnd_end. No indexes are used.
142
*/
520.1.22 by Brian Aker
Second pass of thd cleanup
143
void init_read_record(READ_RECORD *info,Session *session, Table *table,
1 by brian
clean slate
144
		      SQL_SELECT *select,
145
		      int use_record_cache, bool print_error)
146
{
147
  IO_CACHE *tempfile;
148
212.6.6 by Mats Kindahl
Removing redundant use of casts in drizzled/ for memcmp(), memcpy(), memset(), and memmove().
149
  memset(info, 0, sizeof(*info));
520.1.22 by Brian Aker
Second pass of thd cleanup
150
  info->session=session;
1 by brian
clean slate
151
  info->table=table;
152
  info->file= table->file;
153
  info->forms= &info->table;		/* Only one table */
154
  
155
  if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE &&
156
      !table->sort.addon_field)
398.1.10 by Monty Taylor
Actually removed VOID() this time.
157
    table->file->extra(HA_EXTRA_MMAP);
1 by brian
clean slate
158
  
159
  if (table->sort.addon_field)
160
  {
161
    info->rec_buf= table->sort.addon_buf;
162
    info->ref_length= table->sort.addon_length;
163
  }
164
  else
165
  {
166
    empty_record(table);
167
    info->record= table->record[0];
168
    info->ref_length= table->file->ref_length;
169
  }
170
  info->select=select;
171
  info->print_error=print_error;
172
  info->ignore_not_found_rows= 0;
173
  table->status=0;			/* And it's always found */
174
175
  if (select && my_b_inited(&select->file))
176
    tempfile= &select->file;
177
  else
178
    tempfile= table->sort.io_cache;
179
  if (tempfile && my_b_inited(tempfile)) // Test if ref-records was used
180
  {
181
    info->read_record= (table->sort.addon_field ?
182
                        rr_unpack_from_tempfile : rr_from_tempfile);
183
    info->io_cache=tempfile;
184
    reinit_io_cache(info->io_cache,READ_CACHE,0L,0,0);
185
    info->ref_pos=table->file->ref;
186
    if (!table->file->inited)
187
      table->file->ha_rnd_init(0);
188
189
    /*
190
      table->sort.addon_field is checked because if we use addon fields,
191
      it doesn't make sense to use cache - we don't read from the table
192
      and table->sort.io_cache is read sequentially
193
    */
194
    if (!table->sort.addon_field &&
520.1.22 by Brian Aker
Second pass of thd cleanup
195
	session->variables.read_rnd_buff_size &&
1 by brian
clean slate
196
	!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
197
	(table->db_stat & HA_READ_ONLY ||
198
	 table->reginfo.lock_type <= TL_READ_NO_INSERT) &&
151 by Brian Aker
Ulonglong to uint64_t
199
	(uint64_t) table->s->reclength* (table->file->stats.records+
1 by brian
clean slate
200
                                          table->file->stats.deleted) >
151 by Brian Aker
Ulonglong to uint64_t
201
	(uint64_t) MIN_FILE_LENGTH_TO_USE_ROW_CACHE &&
1 by brian
clean slate
202
	info->io_cache->end_of_file/info->ref_length * table->s->reclength >
203
	(my_off_t) MIN_ROWS_TO_USE_TABLE_CACHE &&
204
	!table->s->blob_fields &&
205
        info->ref_length <= MAX_REFLENGTH)
206
    {
520.1.22 by Brian Aker
Second pass of thd cleanup
207
      if (! init_rr_cache(session, info))
1 by brian
clean slate
208
      {
209
	info->read_record=rr_from_cache;
210
      }
211
    }
212
  }
213
  else if (select && select->quick)
214
  {
215
    info->read_record=rr_quick;
216
  }
217
  else if (table->sort.record_pointers)
218
  {
219
    table->file->ha_rnd_init(0);
220
    info->cache_pos=table->sort.record_pointers;
221
    info->cache_end=info->cache_pos+ 
222
                    table->sort.found_records*info->ref_length;
223
    info->read_record= (table->sort.addon_field ?
224
                        rr_unpack_from_buffer : rr_from_pointers);
225
  }
226
  else
227
  {
228
    info->read_record=rr_sequential;
229
    table->file->ha_rnd_init(1);
230
    /* We can use record cache if we don't update dynamic length tables */
231
    if (!table->no_cache &&
232
	(use_record_cache > 0 ||
233
	 (int) table->reginfo.lock_type <= (int) TL_READ_HIGH_PRIORITY ||
234
	 !(table->s->db_options_in_use & HA_OPTION_PACK_RECORD) ||
235
	 (use_record_cache < 0 &&
236
	  !(table->file->ha_table_flags() & HA_NOT_DELETE_WITH_CACHE))))
520.1.22 by Brian Aker
Second pass of thd cleanup
237
      table->file->extra_opt(HA_EXTRA_CACHE, session->variables.read_buff_size);
1 by brian
clean slate
238
  }
239
  /* 
240
    Do condition pushdown for UPDATE/DELETE.
241
    TODO: Remove this from here as it causes two condition pushdown calls 
242
    when we're running a SELECT and the condition cannot be pushed down.
243
  */
520.1.22 by Brian Aker
Second pass of thd cleanup
244
  if (session->variables.engine_condition_pushdown && 
1 by brian
clean slate
245
      select && select->cond && 
246
      (select->cond->used_tables() & table->map) &&
247
      !table->file->pushed_cond)
248
    table->file->cond_push(select->cond);
249
51.1.4 by Jay Pipes
Removed all DBUG_XXX content from records.cc. Builds/tests run fine. On to next file...
250
  return;
1 by brian
clean slate
251
} /* init_read_record */
252
253
254
255
void end_read_record(READ_RECORD *info)
256
{                   /* free cache if used */
257
  if (info->cache)
258
  {
477 by Monty Taylor
Removed my_free(). It turns out that it had been def'd to ignore the flags passed to it in the second arg anyway. Gotta love that.
259
    free((char*) info->cache);
1 by brian
clean slate
260
    info->cache=0;
261
  }
262
  if (info->table)
263
  {
264
    filesort_free_buffers(info->table,0);
265
    (void) info->file->extra(HA_EXTRA_NO_CACHE);
266
    if (info->read_record != rr_quick) // otherwise quick_range does it
267
      (void) info->file->ha_index_or_rnd_end();
268
    info->table=0;
269
  }
270
}
271
272
static int rr_handle_error(READ_RECORD *info, int error)
273
{
274
  if (error == HA_ERR_END_OF_FILE)
275
    error= -1;
276
  else
277
  {
278
    if (info->print_error)
279
      info->table->file->print_error(error, MYF(0));
280
    if (error < 0)                            // Fix negative BDB errno
281
      error= 1;
282
  }
283
  return error;
284
}
285
286
287
/** Read a record from head-database. */
288
289
static int rr_quick(READ_RECORD *info)
290
{
291
  int tmp;
292
  while ((tmp= info->select->quick->get_next()))
293
  {
520.1.22 by Brian Aker
Second pass of thd cleanup
294
    if (info->session->killed)
1 by brian
clean slate
295
    {
296
      my_error(ER_SERVER_SHUTDOWN, MYF(0));
297
      return 1;
298
    }
299
    if (tmp != HA_ERR_RECORD_DELETED)
300
    {
301
      tmp= rr_handle_error(info, tmp);
302
      break;
303
    }
304
  }
383.7.1 by Andrey Zhakov
Initial submit of code and tests
305
  update_virtual_fields_marked_for_write(info->table);
1 by brian
clean slate
306
  return tmp;
307
}
308
309
310
/**
311
  Reads first row in an index scan.
312
313
  @param info  	Scan info
314
315
  @retval
316
    0   Ok
317
  @retval
318
    -1   End of records
319
  @retval
320
    1   Error
321
*/
322
323
static int rr_index_first(READ_RECORD *info)
324
{
325
  int tmp= info->file->index_first(info->record);
326
  info->read_record= rr_index;
327
  if (tmp)
328
    tmp= rr_handle_error(info, tmp);
329
  return tmp;
330
}
331
332
333
/**
334
  Reads index sequentially after first row.
335
336
  Read the next index record (in forward direction) and translate return
337
  value.
338
339
  @param info  Scan info
340
341
  @retval
342
    0   Ok
343
  @retval
344
    -1   End of records
345
  @retval
346
    1   Error
347
*/
348
349
static int rr_index(READ_RECORD *info)
350
{
351
  int tmp= info->file->index_next(info->record);
352
  if (tmp)
353
    tmp= rr_handle_error(info, tmp);
354
  return tmp;
355
}
356
357
358
int rr_sequential(READ_RECORD *info)
359
{
360
  int tmp;
361
  while ((tmp=info->file->rnd_next(info->record)))
362
  {
520.1.22 by Brian Aker
Second pass of thd cleanup
363
    if (info->session->killed)
1 by brian
clean slate
364
    {
520.1.22 by Brian Aker
Second pass of thd cleanup
365
      info->session->send_kill_message();
1 by brian
clean slate
366
      return 1;
367
    }
368
    /*
369
      rnd_next can return RECORD_DELETED for MyISAM when one thread is
370
      reading and another deleting without locks.
371
    */
372
    if (tmp != HA_ERR_RECORD_DELETED)
373
    {
374
      tmp= rr_handle_error(info, tmp);
375
      break;
376
    }
377
  }
383.7.1 by Andrey Zhakov
Initial submit of code and tests
378
  if (!tmp)
379
    update_virtual_fields_marked_for_write(info->table);
1 by brian
clean slate
380
  return tmp;
381
}
382
383
384
static int rr_from_tempfile(READ_RECORD *info)
385
{
386
  int tmp;
387
  for (;;)
388
  {
389
    if (my_b_read(info->io_cache,info->ref_pos,info->ref_length))
390
      return -1;					/* End of file */
391
    if (!(tmp=info->file->rnd_pos(info->record,info->ref_pos)))
392
      break;
393
    /* The following is extremely unlikely to happen */
394
    if (tmp == HA_ERR_RECORD_DELETED ||
395
        (tmp == HA_ERR_KEY_NOT_FOUND && info->ignore_not_found_rows))
396
      continue;
397
    tmp= rr_handle_error(info, tmp);
398
    break;
399
  }
400
  return tmp;
401
} /* rr_from_tempfile */
402
403
404
/**
405
  Read a result set record from a temporary file after sorting.
406
407
  The function first reads the next sorted record from the temporary file.
408
  into a buffer. If a success it calls a callback function that unpacks 
409
  the fields values use in the result set from this buffer into their
410
  positions in the regular record buffer.
411
412
  @param info          Reference to the context including record descriptors
413
414
  @retval
415
    0   Record successfully read.
416
  @retval
417
    -1   There is no record to be read anymore.
418
*/
419
420
static int rr_unpack_from_tempfile(READ_RECORD *info)
421
{
422
  if (my_b_read(info->io_cache, info->rec_buf, info->ref_length))
423
    return -1;
327.1.5 by Brian Aker
Refactor around classes. TABLE_LIST has been factored out of table.h
424
  Table *table= info->table;
1 by brian
clean slate
425
  (*table->sort.unpack)(table->sort.addon_field, info->rec_buf);
426
427
  return 0;
428
}
429
430
static int rr_from_pointers(READ_RECORD *info)
431
{
432
  int tmp;
481 by Brian Aker
Remove all of uchar.
433
  unsigned char *cache_pos;
1 by brian
clean slate
434
435
  for (;;)
436
  {
437
    if (info->cache_pos == info->cache_end)
438
      return -1;					/* End of file */
439
    cache_pos= info->cache_pos;
440
    info->cache_pos+= info->ref_length;
441
442
    if (!(tmp=info->file->rnd_pos(info->record,cache_pos)))
443
      break;
444
445
    /* The following is extremely unlikely to happen */
446
    if (tmp == HA_ERR_RECORD_DELETED ||
447
        (tmp == HA_ERR_KEY_NOT_FOUND && info->ignore_not_found_rows))
448
      continue;
449
    tmp= rr_handle_error(info, tmp);
450
    break;
451
  }
452
  return tmp;
453
}
454
455
/**
456
  Read a result set record from a buffer after sorting.
457
458
  The function first reads the next sorted record from the sort buffer.
459
  If a success it calls a callback function that unpacks 
460
  the fields values use in the result set from this buffer into their
461
  positions in the regular record buffer.
462
463
  @param info          Reference to the context including record descriptors
464
465
  @retval
466
    0   Record successfully read.
467
  @retval
468
    -1   There is no record to be read anymore.
469
*/
470
471
static int rr_unpack_from_buffer(READ_RECORD *info)
472
{
473
  if (info->cache_pos == info->cache_end)
474
    return -1;                      /* End of buffer */
327.1.5 by Brian Aker
Refactor around classes. TABLE_LIST has been factored out of table.h
475
  Table *table= info->table;
1 by brian
clean slate
476
  (*table->sort.unpack)(table->sort.addon_field, info->cache_pos);
477
  info->cache_pos+= info->ref_length;
478
479
  return 0;
480
}
481
	/* cacheing of records from a database */
482
520.1.22 by Brian Aker
Second pass of thd cleanup
483
static int init_rr_cache(Session *session, READ_RECORD *info)
1 by brian
clean slate
484
{
482 by Brian Aker
Remove uint.
485
  uint32_t rec_cache_size;
1 by brian
clean slate
486
487
  info->struct_length= 3+MAX_REFLENGTH;
488
  info->reclength= ALIGN_SIZE(info->table->s->reclength+1);
489
  if (info->reclength < info->struct_length)
490
    info->reclength= ALIGN_SIZE(info->struct_length);
491
492
  info->error_offset= info->table->s->reclength;
520.1.22 by Brian Aker
Second pass of thd cleanup
493
  info->cache_records= (session->variables.read_rnd_buff_size /
1 by brian
clean slate
494
                        (info->reclength+info->struct_length));
495
  rec_cache_size= info->cache_records*info->reclength;
496
  info->rec_cache_size= info->cache_records*info->ref_length;
497
498
  // We have to allocate one more byte to use uint3korr (see comments for it)
499
  if (info->cache_records <= 2 ||
481 by Brian Aker
Remove all of uchar.
500
      !(info->cache=(unsigned char*) my_malloc_lock(rec_cache_size+info->cache_records*
1 by brian
clean slate
501
					   info->struct_length+1,
502
					   MYF(0))))
51.1.4 by Jay Pipes
Removed all DBUG_XXX content from records.cc. Builds/tests run fine. On to next file...
503
    return(1);
1 by brian
clean slate
504
#ifdef HAVE_purify
505
  // Avoid warnings in qsort
212.6.1 by Mats Kindahl
Replacing all bzero() calls with memset() calls and removing the bzero.c file.
506
  memset(info->cache, 0,
507
         rec_cache_size+info->cache_records* info->struct_length+1);
1 by brian
clean slate
508
#endif
509
  info->read_positions=info->cache+rec_cache_size;
510
  info->cache_pos=info->cache_end=info->cache;
51.1.4 by Jay Pipes
Removed all DBUG_XXX content from records.cc. Builds/tests run fine. On to next file...
511
  return(0);
1 by brian
clean slate
512
} /* init_rr_cache */
513
514
515
static int rr_from_cache(READ_RECORD *info)
516
{
482 by Brian Aker
Remove uint.
517
  register uint32_t i;
308 by Brian Aker
ulong conversion
518
  uint32_t length;
1 by brian
clean slate
519
  my_off_t rest_of_file;
206 by Brian Aker
Removed final uint dead types.
520
  int16_t error;
481 by Brian Aker
Remove all of uchar.
521
  unsigned char *position,*ref_position,*record_pos;
308 by Brian Aker
ulong conversion
522
  uint32_t record;
1 by brian
clean slate
523
524
  for (;;)
525
  {
526
    if (info->cache_pos != info->cache_end)
527
    {
528
      if (info->cache_pos[info->error_offset])
529
      {
530
	shortget(error,info->cache_pos);
531
	if (info->print_error)
532
	  info->table->file->print_error(error,MYF(0));
533
      }
534
      else
535
      {
536
	error=0;
537
	memcpy(info->record,info->cache_pos,
538
               (size_t) info->table->s->reclength);
539
      }
540
      info->cache_pos+=info->reclength;
541
      return ((int) error);
542
    }
543
    length=info->rec_cache_size;
544
    rest_of_file=info->io_cache->end_of_file - my_b_tell(info->io_cache);
545
    if ((my_off_t) length > rest_of_file)
308 by Brian Aker
ulong conversion
546
      length= (uint32_t) rest_of_file;
1 by brian
clean slate
547
    if (!length || my_b_read(info->io_cache,info->cache,length))
548
    {
549
      return -1;			/* End of file */
550
    }
551
552
    length/=info->ref_length;
553
    position=info->cache;
554
    ref_position=info->read_positions;
555
    for (i=0 ; i < length ; i++,position+=info->ref_length)
556
    {
557
      memcpy(ref_position,position,(size_t) info->ref_length);
558
      ref_position+=MAX_REFLENGTH;
559
      int3store(ref_position,(long) i);
560
      ref_position+=3;
561
    }
562
    my_qsort(info->read_positions, length, info->struct_length,
563
             (qsort_cmp) rr_cmp);
564
565
    position=info->read_positions;
566
    for (i=0 ; i < length ; i++)
567
    {
568
      memcpy(info->ref_pos,position,(size_t) info->ref_length);
569
      position+=MAX_REFLENGTH;
570
      record=uint3korr(position);
571
      position+=3;
572
      record_pos=info->cache+record*info->reclength;
206 by Brian Aker
Removed final uint dead types.
573
      if ((error=(int16_t) info->file->rnd_pos(record_pos,info->ref_pos)))
1 by brian
clean slate
574
      {
575
	record_pos[info->error_offset]=1;
576
	shortstore(record_pos,error);
577
      }
578
      else
579
	record_pos[info->error_offset]=0;
580
    }
581
    info->cache_end=(info->cache_pos=info->cache)+length*info->reclength;
582
  }
583
} /* rr_from_cache */
584
585
481 by Brian Aker
Remove all of uchar.
586
static int rr_cmp(unsigned char *a,unsigned char *b)
1 by brian
clean slate
587
{
588
  if (a[0] != b[0])
589
    return (int) a[0] - (int) b[0];
590
  if (a[1] != b[1])
591
    return (int) a[1] - (int) b[1];
592
  if (a[2] != b[2])
593
    return (int) a[2] - (int) b[2];
594
#if MAX_REFLENGTH == 4
595
  return (int) a[3] - (int) b[3];
596
#else
597
  if (a[3] != b[3])
598
    return (int) a[3] - (int) b[3];
599
  if (a[4] != b[4])
600
    return (int) a[4] - (int) b[4];
601
  if (a[5] != b[5])
602
    return (int) a[1] - (int) b[5];
603
  if (a[6] != b[6])
604
    return (int) a[6] - (int) b[6];
605
  return (int) a[7] - (int) b[7];
606
#endif
607
}