~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2000 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
/* This file should be included when using myisam_funktions */
17
18
#ifndef _myisam_h
19
#define _myisam_h
20
#ifdef	__cplusplus
21
extern "C" {
22
#endif
23
24
#ifndef _my_base_h
25
#include <my_base.h>
26
#endif
27
#ifndef _m_ctype_h
28
#include <m_ctype.h>
29
#endif
30
#ifndef _keycache_h
31
#include "keycache.h"
32
#endif
33
#include "my_handler.h"
34
#include <mysql/plugin.h>
35
36
/*
37
  Limit max keys according to HA_MAX_POSSIBLE_KEY
38
*/
39
40
#if MAX_INDEXES > HA_MAX_POSSIBLE_KEY
41
#define MI_MAX_KEY                  HA_MAX_POSSIBLE_KEY /* Max allowed keys */
42
#else
43
#define MI_MAX_KEY                  MAX_INDEXES         /* Max allowed keys */
44
#endif
45
46
/*
47
  The following defines can be increased if necessary.
48
  But beware the dependency of MI_MAX_POSSIBLE_KEY_BUFF and MI_MAX_KEY_LENGTH.
49
*/
50
#define MI_MAX_KEY_LENGTH           1332            /* Max length in bytes */
51
#define MI_MAX_KEY_SEG              16              /* Max segments for key */
52
53
#define MI_MAX_POSSIBLE_KEY_BUFF (MI_MAX_KEY_LENGTH + 6 + 6) /* For mi_check */
54
55
#define MI_MAX_KEY_BUFF  (MI_MAX_KEY_LENGTH+MI_MAX_KEY_SEG*6+8+8)
56
#define MI_MAX_MSG_BUF      1024 /* used in CHECK TABLE, REPAIR TABLE */
57
#define MI_NAME_IEXT	".MYI"
58
#define MI_NAME_DEXT	".MYD"
59
/* Max extra space to use when sorting keys */
60
#define MI_MAX_TEMP_LENGTH	2*1024L*1024L*1024L
61
62
/* Possible values for myisam_block_size (must be power of 2) */
63
#define MI_KEY_BLOCK_LENGTH	1024	/* default key block length */
64
#define MI_MIN_KEY_BLOCK_LENGTH	1024	/* Min key block length */
65
#define MI_MAX_KEY_BLOCK_LENGTH	16384
66
67
/*
68
  In the following macros '_keyno_' is 0 .. keys-1.
69
  If there can be more keys than bits in the key_map, the highest bit
70
  is for all upper keys. They cannot be switched individually.
71
  This means that clearing of high keys is ignored, setting one high key
72
  sets all high keys.
73
*/
74
#define MI_KEYMAP_BITS      (8 * SIZEOF_LONG_LONG)
75
#define MI_KEYMAP_HIGH_MASK (ULL(1) << (MI_KEYMAP_BITS - 1))
76
#define mi_get_mask_all_keys_active(_keys_) \
77
                            (((_keys_) < MI_KEYMAP_BITS) ? \
78
                             ((ULL(1) << (_keys_)) - ULL(1)) : \
79
                             (~ ULL(0)))
80
81
#if MI_MAX_KEY > MI_KEYMAP_BITS
82
83
#define mi_is_key_active(_keymap_,_keyno_) \
84
                            (((_keyno_) < MI_KEYMAP_BITS) ? \
85
                             test((_keymap_) & (ULL(1) << (_keyno_))) : \
86
                             test((_keymap_) & MI_KEYMAP_HIGH_MASK))
87
#define mi_set_key_active(_keymap_,_keyno_) \
88
                            (_keymap_)|= (((_keyno_) < MI_KEYMAP_BITS) ? \
89
                                          (ULL(1) << (_keyno_)) : \
90
                                          MI_KEYMAP_HIGH_MASK)
91
#define mi_clear_key_active(_keymap_,_keyno_) \
92
                            (_keymap_)&= (((_keyno_) < MI_KEYMAP_BITS) ? \
93
                                          (~ (ULL(1) << (_keyno_))) : \
94
                                          (~ (ULL(0))) /*ignore*/ )
95
96
#else
97
98
#define mi_is_key_active(_keymap_,_keyno_) \
99
                            test((_keymap_) & (ULL(1) << (_keyno_)))
100
#define mi_set_key_active(_keymap_,_keyno_) \
101
                            (_keymap_)|= (ULL(1) << (_keyno_))
102
#define mi_clear_key_active(_keymap_,_keyno_) \
103
                            (_keymap_)&= (~ (ULL(1) << (_keyno_)))
104
105
#endif
106
107
#define mi_is_any_key_active(_keymap_) \
108
                            test((_keymap_))
109
#define mi_is_all_keys_active(_keymap_,_keys_) \
110
                            ((_keymap_) == mi_get_mask_all_keys_active(_keys_))
111
#define mi_set_all_keys_active(_keymap_,_keys_) \
112
                            (_keymap_)= mi_get_mask_all_keys_active(_keys_)
113
#define mi_clear_all_keys_active(_keymap_) \
114
                            (_keymap_)= 0
115
#define mi_intersect_keys_active(_to_,_from_) \
116
                            (_to_)&= (_from_)
117
#define mi_is_any_intersect_keys_active(_keymap1_,_keys_,_keymap2_) \
118
                            ((_keymap1_) & (_keymap2_) & \
119
                             mi_get_mask_all_keys_active(_keys_))
120
#define mi_copy_keys_active(_to_,_maxkeys_,_from_) \
121
                            (_to_)= (mi_get_mask_all_keys_active(_maxkeys_) & \
122
                                     (_from_))
123
124
	/* Param to/from mi_status */
125
126
typedef struct st_mi_isaminfo		/* Struct from h_info */
127
{
128
  ha_rows records;			/* Records in database */
129
  ha_rows deleted;			/* Deleted records in database */
130
  my_off_t recpos;			/* Pos for last used record */
131
  my_off_t newrecpos;			/* Pos if we write new record */
132
  my_off_t dupp_key_pos;		/* Position to record with dupp key */
133
  my_off_t data_file_length,		/* Length of data file */
134
           max_data_file_length,
135
           index_file_length,
136
           max_index_file_length,
137
           delete_length;
138
  ulong reclength;			/* Recordlength */
139
  ulong mean_reclength;			/* Mean recordlength (if packed) */
140
  ulonglong auto_increment;
141
  ulonglong key_map;			/* Which keys are used */
142
  char  *data_file_name, *index_file_name;
143
  uint  keys;				/* Number of keys in use */
144
  uint	options;			/* HA_OPTION_... used */
145
  int	errkey,				/* With key was dupplicated on err */
146
	sortkey;			/* clustered by this key */
147
  File	filenr;				/* (uniq) filenr for datafile */
148
  time_t create_time;			/* When table was created */
149
  time_t check_time;
150
  time_t update_time;
151
  uint  reflength;
152
  ulong record_offset;
153
  ulong *rec_per_key;			/* for sql optimizing */
154
} MI_ISAMINFO;
155
156
157
typedef struct st_mi_create_info
158
{
159
  const char *index_file_name, *data_file_name;	/* If using symlinks */
160
  ha_rows max_rows;
161
  ha_rows reloc_rows;
162
  ulonglong auto_increment;
163
  ulonglong data_file_length;
164
  ulonglong key_file_length;
165
  uint old_options;
166
  uint8 language;
167
  my_bool with_auto_increment;
168
} MI_CREATE_INFO;
169
170
struct st_myisam_info;			/* For referense */
171
struct st_mi_isam_share;
172
typedef struct st_myisam_info MI_INFO;
173
struct st_mi_s_param;
174
175
typedef struct st_mi_keydef		/* Key definition with open & info */
176
{
177
  struct st_mi_isam_share *share;       /* Pointer to base (set in mi_open) */
178
  uint16 keysegs;			/* Number of key-segment */
179
  uint16 flag;				/* NOSAME, PACK_USED */
180
181
  uint8  key_alg;			/* BTREE, RTREE */
182
  uint16 block_length;			/* Length of keyblock (auto) */
183
  uint16 underflow_block_length;	/* When to execute underflow */
184
  uint16 keylength;			/* Tot length of keyparts (auto) */
185
  uint16 minlength;			/* min length of (packed) key (auto) */
186
  uint16 maxlength;			/* max length of (packed) key (auto) */
187
  uint16 block_size_index;		/* block_size (auto) */
188
  uint32 version;			/* For concurrent read/write */
189
  uint32 ftparser_nr;                   /* distinct ftparser number */
190
191
  HA_KEYSEG *seg,*end;
192
  struct st_mysql_ftparser *parser;     /* Fulltext [pre]parser */
193
  int (*bin_search)(struct st_myisam_info *info,struct st_mi_keydef *keyinfo,
194
		    uchar *page,uchar *key,
195
		    uint key_len,uint comp_flag,uchar * *ret_pos,
196
		    uchar *buff, my_bool *was_last_key);
197
  uint (*get_key)(struct st_mi_keydef *keyinfo,uint nod_flag,uchar * *page,
198
		  uchar *key);
199
  int (*pack_key)(struct st_mi_keydef *keyinfo,uint nod_flag,uchar *next_key,
200
		  uchar *org_key, uchar *prev_key, uchar *key,
201
		  struct st_mi_s_param *s_temp);
202
  void (*store_key)(struct st_mi_keydef *keyinfo, uchar *key_pos,
203
		    struct st_mi_s_param *s_temp);
204
  int (*ck_insert)(struct st_myisam_info *inf, uint k_nr, uchar *k, uint klen);
205
  int (*ck_delete)(struct st_myisam_info *inf, uint k_nr, uchar *k, uint klen);
206
} MI_KEYDEF;
207
208
209
#define MI_UNIQUE_HASH_LENGTH	4
210
211
typedef struct st_unique_def		/* Segment definition of unique */
212
{
213
  uint16 keysegs;			/* Number of key-segment */
214
  uchar key;				/* Mapped to which key */
215
  uint8 null_are_equal;
216
  HA_KEYSEG *seg,*end;
217
} MI_UNIQUEDEF;
218
219
typedef struct st_mi_decode_tree	/* Decode huff-table */
220
{
221
  uint16 *table;
222
  uint	 quick_table_bits;
223
  uchar	 *intervalls;
224
} MI_DECODE_TREE;
225
226
227
struct st_mi_bit_buff;
228
229
/*
230
  Note that null markers should always be first in a row !
231
  When creating a column, one should only specify:
232
  type, length, null_bit and null_pos
233
*/
234
235
typedef struct st_columndef		/* column information */
236
{
237
  int16  type;				/* en_fieldtype */
238
  uint16 length;			/* length of field */
239
  uint32 offset;			/* Offset to position in row */
240
  uint8  null_bit;			/* If column may be 0 */
241
  uint16 null_pos;			/* position for null marker */
242
243
#ifndef NOT_PACKED_DATABASES
244
  void (*unpack)(struct st_columndef *rec,struct st_mi_bit_buff *buff,
245
		 uchar *start,uchar *end);
246
  enum en_fieldtype base_type;
247
  uint space_length_bits,pack_type;
248
  MI_DECODE_TREE *huff_tree;
249
#endif
250
} MI_COLUMNDEF;
251
252
253
extern char * myisam_log_filename;		/* Name of logfile */
254
extern ulong myisam_block_size;
255
extern ulong myisam_concurrent_insert;
256
extern my_bool myisam_flush,myisam_delay_key_write,myisam_single_user;
257
extern my_off_t myisam_max_temp_length;
258
extern ulong myisam_bulk_insert_tree_size, myisam_data_pointer_size;
259
260
	/* Prototypes for myisam-functions */
261
262
extern int mi_close(struct st_myisam_info *file);
263
extern int mi_delete(struct st_myisam_info *file,const uchar *buff);
264
extern struct st_myisam_info *mi_open(const char *name,int mode,
265
				      uint wait_if_locked);
266
extern int mi_panic(enum ha_panic_function function);
267
extern int mi_rfirst(struct st_myisam_info *file,uchar *buf,int inx);
268
extern int mi_rkey(MI_INFO *info, uchar *buf, int inx, const uchar *key,
269
                   key_part_map keypart_map, enum ha_rkey_function search_flag);
270
extern int mi_rlast(struct st_myisam_info *file,uchar *buf,int inx);
271
extern int mi_rnext(struct st_myisam_info *file,uchar *buf,int inx);
272
extern int mi_rnext_same(struct st_myisam_info *info, uchar *buf);
273
extern int mi_rprev(struct st_myisam_info *file,uchar *buf,int inx);
274
extern int mi_rrnd(struct st_myisam_info *file,uchar *buf, my_off_t pos);
275
extern int mi_scan_init(struct st_myisam_info *file);
276
extern int mi_scan(struct st_myisam_info *file,uchar *buf);
277
extern int mi_rsame(struct st_myisam_info *file,uchar *record,int inx);
278
extern int mi_rsame_with_pos(struct st_myisam_info *file,uchar *record,
279
			     int inx, my_off_t pos);
280
extern int mi_update(struct st_myisam_info *file,const uchar *old,
281
		     uchar *new_record);
282
extern int mi_write(struct st_myisam_info *file,uchar *buff);
283
extern my_off_t mi_position(struct st_myisam_info *file);
284
extern int mi_status(struct st_myisam_info *info, MI_ISAMINFO *x, uint flag);
285
extern int mi_lock_database(struct st_myisam_info *file,int lock_type);
286
extern int mi_create(const char *name,uint keys,MI_KEYDEF *keydef,
287
		     uint columns, MI_COLUMNDEF *columndef,
288
		     uint uniques, MI_UNIQUEDEF *uniquedef,
289
		     MI_CREATE_INFO *create_info, uint flags);
290
extern int mi_delete_table(const char *name);
291
extern int mi_rename(const char *from, const char *to);
292
extern int mi_extra(struct st_myisam_info *file,
293
		    enum ha_extra_function function,
294
		    void *extra_arg);
295
extern int mi_reset(struct st_myisam_info *file);
296
extern ha_rows mi_records_in_range(MI_INFO *info, int inx,
297
                                   key_range *min_key, key_range *max_key);
298
extern int mi_log(int activate_log);
299
extern int mi_is_changed(struct st_myisam_info *info);
300
extern int mi_delete_all_rows(struct st_myisam_info *info);
301
extern ulong _mi_calc_blob_length(uint length , const uchar *pos);
302
extern uint mi_get_pointer_length(ulonglong file_length, uint def);
303
304
/* this is used to pass to mysql_myisamchk_table */
305
306
#define   MYISAMCHK_REPAIR 1  /* equivalent to myisamchk -r */
307
#define   MYISAMCHK_VERIFY 2  /* Verify, run repair if failure */
308
309
/*
310
  Definitions needed for myisamchk.c
311
312
  Entries marked as "QQ to be removed" are NOT used to
313
  pass check/repair options to mi_check.c. They are used
314
  internally by myisamchk.c or/and ha_myisam.cc and should NOT
315
  be stored together with other flags. They should be removed
316
  from the following list to make addition of new flags possible.
317
*/
318
319
#define T_AUTO_INC              1
320
#define T_AUTO_REPAIR           2              /* QQ to be removed */
321
#define T_BACKUP_DATA           4
322
#define T_CALC_CHECKSUM         8
323
#define T_CHECK                 16             /* QQ to be removed */
324
#define T_CHECK_ONLY_CHANGED    32             /* QQ to be removed */
325
#define T_CREATE_MISSING_KEYS   64
326
#define T_DESCRIPT              128
327
#define T_DONT_CHECK_CHECKSUM   256
328
#define T_EXTEND                512
329
#define T_FAST                  (1L << 10)     /* QQ to be removed */
330
#define T_FORCE_CREATE          (1L << 11)     /* QQ to be removed */
331
#define T_FORCE_UNIQUENESS      (1L << 12)
332
#define T_INFO                  (1L << 13)
333
#define T_MEDIUM                (1L << 14)
334
#define T_QUICK                 (1L << 15)     /* QQ to be removed */
335
#define T_READONLY              (1L << 16)     /* QQ to be removed */
336
#define T_REP                   (1L << 17)
337
#define T_REP_BY_SORT           (1L << 18)     /* QQ to be removed */
338
#define T_REP_PARALLEL          (1L << 19)     /* QQ to be removed */
339
#define T_RETRY_WITHOUT_QUICK   (1L << 20)
340
#define T_SAFE_REPAIR           (1L << 21)
341
#define T_SILENT                (1L << 22)
342
#define T_SORT_INDEX            (1L << 23)     /* QQ to be removed */
343
#define T_SORT_RECORDS          (1L << 24)     /* QQ to be removed */
344
#define T_STATISTICS            (1L << 25)
345
#define T_UNPACK                (1L << 26)
346
#define T_UPDATE_STATE          (1L << 27)
347
#define T_VERBOSE               (1L << 28)
348
#define T_VERY_SILENT           (1L << 29)
349
#define T_WAIT_FOREVER          (1L << 30)
350
#define T_WRITE_LOOP            ((ulong) 1L << 31)
351
352
#define T_REP_ANY               (T_REP | T_REP_BY_SORT | T_REP_PARALLEL)
353
354
/*
355
  Flags used by myisamchk.c or/and ha_myisam.cc that are NOT passed
356
  to mi_check.c follows:
357
*/
358
359
#define TT_USEFRM               1
360
#define TT_FOR_UPGRADE          2
361
362
#define O_NEW_INDEX	1		/* Bits set in out_flag */
363
#define O_NEW_DATA	2
364
#define O_DATA_LOST	4
365
366
/* these struct is used by my_check to tell it what to do */
367
368
typedef struct st_sort_key_blocks		/* Used when sorting */
369
{
370
  uchar *buff,*end_pos;
371
  uchar lastkey[MI_MAX_POSSIBLE_KEY_BUFF];
372
  uint last_length;
373
  int inited;
374
} SORT_KEY_BLOCKS;
375
376
377
/* 
378
  MyISAM supports several statistics collection methods. Currently statistics 
379
  collection method is not stored in MyISAM file and has to be specified for 
380
  each table analyze/repair operation in  MI_CHECK::stats_method.
381
*/
382
383
typedef enum 
384
{
385
  /* Treat NULLs as inequal when collecting statistics (default for 4.1/5.0) */
386
  MI_STATS_METHOD_NULLS_NOT_EQUAL,
387
  /* Treat NULLs as equal when collecting statistics (like 4.0 did) */
388
  MI_STATS_METHOD_NULLS_EQUAL,
389
  /* Ignore NULLs - count only tuples without NULLs in the index components */
390
  MI_STATS_METHOD_IGNORE_NULLS
391
} enum_mi_stats_method;
392
393
typedef struct st_mi_check_param
394
{
395
  ulonglong auto_increment_value;
396
  ulonglong max_data_file_length;
397
  ulonglong keys_in_use;
398
  ulonglong max_record_length;
399
  my_off_t search_after_block;
400
  my_off_t new_file_pos,key_file_blocks;
401
  my_off_t keydata,totaldata,key_blocks,start_check_pos;
402
  ha_rows total_records,total_deleted;
403
  ha_checksum record_checksum,glob_crc;
404
  ulong	use_buffers,read_buffer_length,write_buffer_length,
405
	sort_buffer_length,sort_key_blocks;
406
  uint out_flag,warning_printed,error_printed,verbose;
407
  uint opt_sort_key,total_files,max_level;
408
  uint testflag, key_cache_block_size;
409
  uint8 language;
410
  my_bool using_global_keycache, opt_lock_memory, opt_follow_links;
411
  my_bool retry_repair, force_sort;
412
  char temp_filename[FN_REFLEN],*isam_file_name;
413
  MY_TMPDIR *tmpdir;
414
  int tmpfile_createflag;
415
  myf myf_rw;
416
  IO_CACHE read_cache;
417
  
418
  /* 
419
    The next two are used to collect statistics, see update_key_parts for
420
    description.
421
  */
422
  ulonglong unique_count[MI_MAX_KEY_SEG+1];
423
  ulonglong notnull_count[MI_MAX_KEY_SEG+1];
424
  
425
  ha_checksum key_crc[HA_MAX_POSSIBLE_KEY];
426
  ulong rec_per_key_part[MI_MAX_KEY_SEG*HA_MAX_POSSIBLE_KEY];
427
  void *thd;
428
  const char *db_name, *table_name;
429
  const char *op_name;
430
  enum_mi_stats_method stats_method;
431
} MI_CHECK;
432
433
typedef struct st_sort_ft_buf
434
{
435
  uchar *buf, *end;
436
  int   count;
437
  uchar lastkey[MI_MAX_KEY_BUFF];
438
} SORT_FT_BUF;
439
440
typedef struct st_sort_info
441
{
442
  my_off_t filelength,dupp,buff_length;
443
  ha_rows max_records;
444
  uint current_key, total_keys;
445
  myf myf_rw;
446
  enum data_file_type new_data_file_type;
447
  MI_INFO *info;
448
  MI_CHECK *param;
449
  uchar *buff;
450
  SORT_KEY_BLOCKS *key_block,*key_block_end;
451
  SORT_FT_BUF *ft_buf;
452
  /* sync things */
453
  uint got_error, threads_running;
454
#ifdef THREAD
455
  pthread_mutex_t mutex;
456
  pthread_cond_t  cond;
457
#endif
458
} SORT_INFO;
459
460
/* functions in mi_check */
461
void myisamchk_init(MI_CHECK *param);
462
int chk_status(MI_CHECK *param, MI_INFO *info);
463
int chk_del(MI_CHECK *param, register MI_INFO *info, uint test_flag);
464
int chk_size(MI_CHECK *param, MI_INFO *info);
465
int chk_key(MI_CHECK *param, MI_INFO *info);
466
int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend);
467
int mi_repair(MI_CHECK *param, register MI_INFO *info,
468
	      char * name, int rep_quick);
469
int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name);
470
int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
471
		      const char * name, int rep_quick);
472
int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
473
		      const char * name, int rep_quick);
474
int change_to_newfile(const char * filename, const char * old_ext,
475
		      const char * new_ext, uint raid_chunks,
476
		      myf myflags);
477
int lock_file(MI_CHECK *param, File file, my_off_t start, int lock_type,
478
	      const char *filetype, const char *filename);
479
void lock_memory(MI_CHECK *param);
480
void update_auto_increment_key(MI_CHECK *param, MI_INFO *info,
481
			       my_bool repair);
482
int update_state_info(MI_CHECK *param, MI_INFO *info,uint update);
483
void update_key_parts(MI_KEYDEF *keyinfo, ulong *rec_per_key_part,
484
                      ulonglong *unique, ulonglong *notnull, 
485
                      ulonglong records);
486
int filecopy(MI_CHECK *param, File to,File from,my_off_t start,
487
	     my_off_t length, const char *type);
488
int movepoint(MI_INFO *info,uchar *record,my_off_t oldpos,
489
	      my_off_t newpos, uint prot_key);
490
int write_data_suffix(SORT_INFO *sort_info, my_bool fix_datafile);
491
int test_if_almost_full(MI_INFO *info);
492
int recreate_table(MI_CHECK *param, MI_INFO **org_info, char *filename);
493
void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows);
494
my_bool mi_test_if_sort_rep(MI_INFO *info, ha_rows rows, ulonglong key_map,
495
			    my_bool force);
496
497
int mi_init_bulk_insert(MI_INFO *info, ulong cache_size, ha_rows rows);
498
void mi_flush_bulk_insert(MI_INFO *info, uint inx);
499
void mi_end_bulk_insert(MI_INFO *info);
500
int mi_assign_to_key_cache(MI_INFO *info, ulonglong key_map, 
501
			   KEY_CACHE *key_cache);
502
void mi_change_key_cache(KEY_CACHE *old_key_cache,
503
			 KEY_CACHE *new_key_cache);
504
int mi_preload(MI_INFO *info, ulonglong key_map, my_bool ignore_leaves);
505
506
#ifdef	__cplusplus
507
}
508
#endif
509
#endif