| 1 | /* -*- c-basic-offset: 2 -*- */ |
| 2 | /* |
| 3 | Copyright(C) 2009-2017 Brazil |
| 4 | |
| 5 | This library is free software; you can redistribute it and/or |
| 6 | modify it under the terms of the GNU Lesser General Public |
| 7 | License version 2.1 as published by the Free Software Foundation. |
| 8 | |
| 9 | This library is distributed in the hope that it will be useful, |
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | Lesser General Public License for more details. |
| 13 | |
| 14 | You should have received a copy of the GNU Lesser General Public |
| 15 | License along with this library; if not, write to the Free Software |
| 16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 17 | */ |
| 18 | |
| 19 | #pragma once |
| 20 | |
| 21 | #include "grn.h" |
| 22 | #include "grn_error.h" |
| 23 | |
| 24 | #ifdef __cplusplus |
| 25 | extern "C" { |
| 26 | #endif |
| 27 | |
| 28 | #ifdef WIN32 |
| 29 | # define GRN_IO_FILE_CREATE_MODE (GENERIC_READ | GENERIC_WRITE) |
| 30 | #else /* WIN32 */ |
| 31 | # define GRN_IO_FILE_CREATE_MODE 0644 |
| 32 | #endif /* WIN32 */ |
| 33 | |
| 34 | typedef enum { |
| 35 | grn_io_rdonly, |
| 36 | grn_io_wronly, |
| 37 | grn_io_rdwr |
| 38 | } grn_io_rw_mode; |
| 39 | |
| 40 | typedef enum { |
| 41 | grn_io_auto, |
| 42 | grn_io_manual |
| 43 | } grn_io_mode; |
| 44 | |
| 45 | /**** grn_io ****/ |
| 46 | |
| 47 | typedef struct _grn_io grn_io; |
| 48 | |
| 49 | typedef struct { |
| 50 | grn_io *io; |
| 51 | grn_ctx *ctx; |
| 52 | uint8_t mode; |
| 53 | uint8_t tiny_p; |
| 54 | uint32_t pseg; |
| 55 | uint32_t segment; |
| 56 | uint32_t offset; |
| 57 | uint32_t size; |
| 58 | uint32_t nseg; |
| 59 | off_t pos; |
| 60 | void *addr; |
| 61 | uint32_t diff; |
| 62 | int32_t cached; |
| 63 | #ifdef WIN32 |
| 64 | HANDLE fmo; |
| 65 | #endif /* WIN32 */ |
| 66 | void *uncompressed_value; |
| 67 | } grn_io_win; |
| 68 | |
| 69 | typedef struct { |
| 70 | void *map; |
| 71 | uint32_t nref; |
| 72 | uint32_t count; |
| 73 | #ifdef WIN32 |
| 74 | HANDLE fmo; |
| 75 | #endif /* WIN32 */ |
| 76 | } grn_io_mapinfo; |
| 77 | |
| 78 | typedef struct _grn_io_array_info grn_io_array_info; |
| 79 | |
| 80 | struct { |
| 81 | char [16]; |
| 82 | uint32_t ; |
| 83 | uint32_t ; |
| 84 | uint32_t ; |
| 85 | uint32_t ; |
| 86 | uint32_t ; |
| 87 | uint32_t ; |
| 88 | uint32_t ; |
| 89 | uint32_t ; |
| 90 | uint64_t ; |
| 91 | uint32_t ; |
| 92 | uint32_t ; |
| 93 | }; |
| 94 | |
| 95 | struct _grn_io { |
| 96 | char path[PATH_MAX]; |
| 97 | struct _grn_io_header *; |
| 98 | byte *; |
| 99 | grn_io_mapinfo *maps; |
| 100 | uint32_t base; |
| 101 | uint32_t base_seg; |
| 102 | grn_io_mode mode; |
| 103 | struct _grn_io_fileinfo *fis; |
| 104 | grn_io_array_info *ainfo; |
| 105 | uint32_t max_map_seg; |
| 106 | uint32_t nmaps; |
| 107 | uint32_t nref; |
| 108 | uint32_t count; |
| 109 | uint8_t flags; |
| 110 | uint32_t *lock; |
| 111 | }; |
| 112 | |
| 113 | GRN_API grn_io *grn_io_create(grn_ctx *ctx, const char *path, |
| 114 | uint32_t , uint32_t segment_size, |
| 115 | uint32_t max_segment, grn_io_mode mode, |
| 116 | unsigned int flags); |
| 117 | grn_io *grn_io_open(grn_ctx *ctx, const char *path, grn_io_mode mode); |
| 118 | GRN_API grn_rc grn_io_close(grn_ctx *ctx, grn_io *io); |
| 119 | grn_rc grn_io_remove(grn_ctx *ctx, const char *path); |
| 120 | grn_rc grn_io_remove_if_exist(grn_ctx *ctx, const char *path); |
| 121 | grn_rc grn_io_size(grn_ctx *ctx, grn_io *io, uint64_t *size); |
| 122 | grn_rc grn_io_rename(grn_ctx *ctx, const char *old_name, const char *new_name); |
| 123 | GRN_API void *(grn_io *io); |
| 124 | |
| 125 | void *grn_io_win_map(grn_io *io, grn_ctx *ctx, grn_io_win *iw, uint32_t segment, |
| 126 | uint32_t offset, uint32_t size, grn_io_rw_mode mode); |
| 127 | grn_rc grn_io_win_unmap(grn_io_win *iw); |
| 128 | |
| 129 | typedef struct _grn_io_ja_einfo grn_io_ja_einfo; |
| 130 | typedef struct _grn_io_ja_ehead grn_io_ja_ehead; |
| 131 | |
| 132 | struct _grn_io_ja_einfo { |
| 133 | uint32_t pos; |
| 134 | uint32_t size; |
| 135 | }; |
| 136 | |
| 137 | struct _grn_io_ja_ehead { |
| 138 | uint32_t size; |
| 139 | uint32_t key; |
| 140 | }; |
| 141 | |
| 142 | grn_rc grn_io_read_ja(grn_io *io, grn_ctx *ctx, grn_io_ja_einfo *einfo, uint32_t epos, |
| 143 | uint32_t key, uint32_t segment, uint32_t offset, |
| 144 | void **value, uint32_t *value_len); |
| 145 | grn_rc grn_io_write_ja(grn_io *io, grn_ctx *ctx, |
| 146 | uint32_t key, uint32_t segment, uint32_t offset, |
| 147 | void *value, uint32_t value_len); |
| 148 | |
| 149 | grn_rc grn_io_write_ja_ehead(grn_io *io, grn_ctx *ctx, uint32_t key, |
| 150 | uint32_t segment, uint32_t offset, uint32_t value_len); |
| 151 | |
| 152 | #define GRN_TABLE_ADD (0x01<<6) |
| 153 | #define GRN_TABLE_ADDED (0x01<<7) |
| 154 | |
| 155 | #define GRN_IO_MAX_RETRY (0x10000) |
| 156 | #define GRN_IO_MAX_REF (0x80000000) |
| 157 | |
| 158 | #define GRN_IO_EXPIRE_GTICK (0x01) |
| 159 | #define GRN_IO_EXPIRE_SEGMENT (0x02) |
| 160 | #define GRN_IO_TEMPORARY (0x04) |
| 161 | |
| 162 | void grn_io_seg_map_(grn_ctx *ctx, grn_io *io, uint32_t segno, grn_io_mapinfo *info); |
| 163 | |
| 164 | /* arguments must be validated by caller; |
| 165 | * io mustn't be NULL; |
| 166 | * segno must be in valid range; |
| 167 | * addr must be set NULL; |
| 168 | */ |
| 169 | #define GRN_IO_SEG_REF(io,segno,addr) do {\ |
| 170 | grn_io_mapinfo *info = &(io)->maps[segno];\ |
| 171 | uint32_t nref, retry, *pnref = &info->nref;\ |
| 172 | if (io->flags & GRN_IO_EXPIRE_SEGMENT) {\ |
| 173 | if (io->flags & GRN_IO_EXPIRE_GTICK) {\ |
| 174 | for (retry = 0; !info->map || info->count != grn_gtick; retry++) {\ |
| 175 | GRN_ATOMIC_ADD_EX(pnref, 1, nref);\ |
| 176 | if (nref) {\ |
| 177 | GRN_ATOMIC_ADD_EX(pnref, -1, nref);\ |
| 178 | if (retry >= GRN_IO_MAX_RETRY) {\ |
| 179 | GRN_LOG(ctx, GRN_LOG_CRIT,\ |
| 180 | "deadlock detected! in GRN_IO_SEG_REF(%p, %u)", io, segno);\ |
| 181 | break;\ |
| 182 | }\ |
| 183 | GRN_FUTEX_WAIT(pnref);\ |
| 184 | } else {\ |
| 185 | info->count = grn_gtick;\ |
| 186 | if (!info->map) {\ |
| 187 | grn_io_seg_map_(ctx, io, segno, info);\ |
| 188 | if (!info->map) {\ |
| 189 | GRN_LOG(ctx, GRN_LOG_CRIT,\ |
| 190 | "mmap failed! in GRN_IO_SEG_REF(%p, %u): %s",\ |
| 191 | io, segno, grn_current_error_message());\ |
| 192 | }\ |
| 193 | }\ |
| 194 | GRN_ATOMIC_ADD_EX(pnref, -1, nref);\ |
| 195 | GRN_FUTEX_WAKE(pnref);\ |
| 196 | break;\ |
| 197 | }\ |
| 198 | }\ |
| 199 | } else {\ |
| 200 | for (retry = 0;; retry++) {\ |
| 201 | GRN_ATOMIC_ADD_EX(pnref, 1, nref);\ |
| 202 | if (nref >= GRN_IO_MAX_REF) {\ |
| 203 | GRN_ATOMIC_ADD_EX(pnref, -1, nref);\ |
| 204 | if (retry >= GRN_IO_MAX_RETRY) {\ |
| 205 | GRN_LOG(ctx, GRN_LOG_CRIT,\ |
| 206 | "deadlock detected!! in GRN_IO_SEG_REF(%p, %u, %u)",\ |
| 207 | io, segno, nref);\ |
| 208 | *pnref = 0; /* force reset */ \ |
| 209 | break;\ |
| 210 | }\ |
| 211 | GRN_FUTEX_WAIT(pnref);\ |
| 212 | continue;\ |
| 213 | }\ |
| 214 | if (nref >= 0x40000000) {\ |
| 215 | ALERT("strange nref value!! in GRN_IO_SEG_REF(%p, %u, %u)",\ |
| 216 | io, segno, nref); \ |
| 217 | }\ |
| 218 | if (!info->map) {\ |
| 219 | if (nref) {\ |
| 220 | GRN_ATOMIC_ADD_EX(pnref, -1, nref);\ |
| 221 | if (retry >= GRN_IO_MAX_RETRY) {\ |
| 222 | GRN_LOG(ctx, GRN_LOG_CRIT,\ |
| 223 | "deadlock detected!!! in GRN_IO_SEG_REF(%p, %u, %u)",\ |
| 224 | io, segno, nref);\ |
| 225 | break;\ |
| 226 | }\ |
| 227 | GRN_FUTEX_WAIT(pnref);\ |
| 228 | continue;\ |
| 229 | } else {\ |
| 230 | grn_io_seg_map_(ctx, io, segno, info);\ |
| 231 | if (!info->map) {\ |
| 232 | GRN_ATOMIC_ADD_EX(pnref, -1, nref);\ |
| 233 | GRN_LOG(ctx, GRN_LOG_CRIT,\ |
| 234 | "mmap failed!!! in GRN_IO_SEG_REF(%p, %u, %u): %s",\ |
| 235 | io, segno, nref, grn_current_error_message());\ |
| 236 | }\ |
| 237 | \ |
| 238 | GRN_FUTEX_WAKE(pnref);\ |
| 239 | }\ |
| 240 | }\ |
| 241 | break;\ |
| 242 | }\ |
| 243 | info->count = grn_gtick;\ |
| 244 | }\ |
| 245 | } else {\ |
| 246 | for (retry = 0; !info->map; retry++) {\ |
| 247 | GRN_ATOMIC_ADD_EX(pnref, 1, nref);\ |
| 248 | if (nref) {\ |
| 249 | GRN_ATOMIC_ADD_EX(pnref, -1, nref);\ |
| 250 | if (retry >= GRN_IO_MAX_RETRY) {\ |
| 251 | GRN_LOG(ctx, GRN_LOG_CRIT,\ |
| 252 | "deadlock detected!!!! in GRN_IO_SEG_REF(%p, %u)",\ |
| 253 | io, segno);\ |
| 254 | break;\ |
| 255 | }\ |
| 256 | GRN_FUTEX_WAIT(pnref);\ |
| 257 | } else {\ |
| 258 | if (!info->map) {\ |
| 259 | grn_io_seg_map_(ctx, io, segno, info);\ |
| 260 | if (!info->map) {\ |
| 261 | GRN_LOG(ctx, GRN_LOG_CRIT,\ |
| 262 | "mmap failed!!!! in GRN_IO_SEG_REF(%p, %u): %s",\ |
| 263 | io, segno, grn_current_error_message());\ |
| 264 | }\ |
| 265 | }\ |
| 266 | GRN_ATOMIC_ADD_EX(pnref, -1, nref);\ |
| 267 | GRN_FUTEX_WAKE(pnref);\ |
| 268 | break;\ |
| 269 | }\ |
| 270 | }\ |
| 271 | info->count = grn_gtick;\ |
| 272 | }\ |
| 273 | addr = info->map;\ |
| 274 | } while (0) |
| 275 | |
| 276 | #define GRN_IO_SEG_UNREF(io,segno) do {\ |
| 277 | if (GRN_IO_EXPIRE_SEGMENT ==\ |
| 278 | (io->flags & (GRN_IO_EXPIRE_GTICK|GRN_IO_EXPIRE_SEGMENT))) {\ |
| 279 | uint32_t nref, *pnref = &(io)->maps[segno].nref;\ |
| 280 | GRN_ATOMIC_ADD_EX(pnref, -1, nref);\ |
| 281 | }\ |
| 282 | } while (0) |
| 283 | |
| 284 | uint32_t grn_io_base_seg(grn_io *io); |
| 285 | const char *grn_io_path(grn_io *io); |
| 286 | |
| 287 | typedef struct _grn_io_array_spec grn_io_array_spec; |
| 288 | |
| 289 | struct _grn_io_array_spec { |
| 290 | uint32_t w_of_element; |
| 291 | uint32_t max_n_segments; |
| 292 | }; |
| 293 | |
| 294 | struct _grn_io_array_info { |
| 295 | uint32_t w_of_elm_in_a_segment; |
| 296 | uint32_t elm_mask_in_a_segment; |
| 297 | uint32_t max_n_segments; |
| 298 | uint32_t element_size; |
| 299 | uint32_t *segments; |
| 300 | void **addrs; |
| 301 | }; |
| 302 | |
| 303 | grn_io *grn_io_create_with_array(grn_ctx *ctx, const char *path, uint32_t , |
| 304 | uint32_t segment_size, grn_io_mode mode, |
| 305 | int n_arrays, grn_io_array_spec *array_specs); |
| 306 | |
| 307 | void *grn_io_array_at(grn_ctx *ctx, grn_io *io, uint32_t array, off_t offset, int *flags); |
| 308 | |
| 309 | void grn_io_segment_alloc(grn_ctx *ctx, grn_io *io, grn_io_array_info *ai, |
| 310 | uint32_t lseg, int *flags, void **p); |
| 311 | |
| 312 | GRN_API grn_rc grn_io_lock(grn_ctx *ctx, grn_io *io, int timeout); |
| 313 | GRN_API void grn_io_unlock(grn_io *io); |
| 314 | void grn_io_clear_lock(grn_io *io); |
| 315 | uint32_t grn_io_is_locked(grn_io *io); |
| 316 | grn_bool grn_io_is_corrupt(grn_ctx *ctx, grn_io *io); |
| 317 | size_t grn_io_get_disk_usage(grn_ctx *ctx, grn_io *io); |
| 318 | |
| 319 | #define GRN_IO_ARRAY_AT(io,array,offset,flags,res) do {\ |
| 320 | grn_io_array_info *ainfo = &(io)->ainfo[array];\ |
| 321 | uint32_t lseg = (offset) >> ainfo->w_of_elm_in_a_segment;\ |
| 322 | void **p_ = &ainfo->addrs[lseg];\ |
| 323 | if (!*p_) {\ |
| 324 | grn_io_segment_alloc(ctx, (io), ainfo, lseg, (flags), p_);\ |
| 325 | if (!*p_) { (res) = NULL; break; }\ |
| 326 | }\ |
| 327 | *((byte **)(&(res))) = (((byte *)*p_) + \ |
| 328 | (((offset) & ainfo->elm_mask_in_a_segment) * ainfo->element_size));\ |
| 329 | } while (0) |
| 330 | |
| 331 | #define GRN_IO_ARRAY_BIT_AT(io,array,offset,res) do {\ |
| 332 | uint8_t *ptr_;\ |
| 333 | int flags_ = 0;\ |
| 334 | GRN_IO_ARRAY_AT((io), (array), ((offset) >> 3) + 1, &flags_, ptr_);\ |
| 335 | res = ptr_ ? ((*ptr_ >> ((offset) & 7)) & 1) : 0;\ |
| 336 | } while (0) |
| 337 | |
| 338 | #define GRN_IO_ARRAY_BIT_ON(io,array,offset) do {\ |
| 339 | uint8_t *ptr_;\ |
| 340 | int flags_ = GRN_TABLE_ADD;\ |
| 341 | GRN_IO_ARRAY_AT((io), (array), ((offset) >> 3) + 1, &flags_, ptr_);\ |
| 342 | if (ptr_) { *ptr_ |= (1 << ((offset) & 7)); }\ |
| 343 | } while (0) |
| 344 | |
| 345 | #define GRN_IO_ARRAY_BIT_OFF(io,array,offset) do {\ |
| 346 | uint8_t *ptr_;\ |
| 347 | int flags_ = GRN_TABLE_ADD;\ |
| 348 | GRN_IO_ARRAY_AT((io), (array), ((offset) >> 3) + 1, &flags_, ptr_);\ |
| 349 | if (ptr_) { *ptr_ &= ~(1 << ((offset) & 7)); }\ |
| 350 | } while (0) |
| 351 | |
| 352 | #define GRN_IO_ARRAY_BIT_FLIP(io,array,offset) do {\ |
| 353 | uint8_t *ptr_;\ |
| 354 | int flags_ = GRN_TABLE_ADD;\ |
| 355 | GRN_IO_ARRAY_AT((io), (array), ((offset) >> 3) + 1, &flags_, ptr_);\ |
| 356 | if (ptr_) { *ptr_ ^= (1 << ((offset) & 7)); }\ |
| 357 | } while (0) |
| 358 | |
| 359 | void *grn_io_anon_map(grn_ctx *ctx, grn_io_mapinfo *mi, size_t length); |
| 360 | void grn_io_anon_unmap(grn_ctx *ctx, grn_io_mapinfo *mi, size_t length); |
| 361 | uint32_t grn_io_detect_type(grn_ctx *ctx, const char *path); |
| 362 | grn_rc grn_io_set_type(grn_io *io, uint32_t type); |
| 363 | uint32_t grn_io_get_type(grn_io *io); |
| 364 | |
| 365 | void grn_io_init_from_env(void); |
| 366 | |
| 367 | uint32_t grn_io_expire(grn_ctx *ctx, grn_io *io, int count_thresh, uint32_t limit); |
| 368 | |
| 369 | grn_rc grn_io_flush(grn_ctx *ctx, grn_io *io); |
| 370 | |
| 371 | /* encode/decode */ |
| 372 | |
| 373 | #define GRN_B_ENC(v,p) do {\ |
| 374 | uint8_t *_p = (uint8_t *)p; \ |
| 375 | uint32_t _v = v; \ |
| 376 | if (_v < 0x8f) { \ |
| 377 | *_p++ = _v; \ |
| 378 | } else if (_v < 0x408f) { \ |
| 379 | _v -= 0x8f; \ |
| 380 | *_p++ = 0xc0 + (_v >> 8); \ |
| 381 | *_p++ = _v & 0xff; \ |
| 382 | } else if (_v < 0x20408f) { \ |
| 383 | _v -= 0x408f; \ |
| 384 | *_p++ = 0xa0 + (_v >> 16); \ |
| 385 | *_p++ = (_v >> 8) & 0xff; \ |
| 386 | *_p++ = _v & 0xff; \ |
| 387 | } else if (_v < 0x1020408f) { \ |
| 388 | _v -= 0x20408f; \ |
| 389 | *_p++ = 0x90 + (_v >> 24); \ |
| 390 | *_p++ = (_v >> 16) & 0xff; \ |
| 391 | *_p++ = (_v >> 8) & 0xff; \ |
| 392 | *_p++ = _v & 0xff; \ |
| 393 | } else { \ |
| 394 | *_p++ = 0x8f; \ |
| 395 | grn_memcpy(_p, &_v, sizeof(uint32_t));\ |
| 396 | _p += sizeof(uint32_t); \ |
| 397 | } \ |
| 398 | p = _p; \ |
| 399 | } while (0) |
| 400 | |
| 401 | #define GRN_B_ENC_SIZE(v) \ |
| 402 | ((v) < 0x8f ? 1 : ((v) < 0x408f ? 2 : ((v) < 0x20408f ? 3 : ((v) < 0x1020408f ? 4 : 5)))) |
| 403 | |
| 404 | #define GRN_B_DEC(v,p) do { \ |
| 405 | uint8_t *_p = (uint8_t *)p; \ |
| 406 | uint32_t _v = *_p++; \ |
| 407 | switch (_v >> 4) { \ |
| 408 | case 0x08 : \ |
| 409 | if (_v == 0x8f) { \ |
| 410 | grn_memcpy(&_v, _p, sizeof(uint32_t));\ |
| 411 | _p += sizeof(uint32_t); \ |
| 412 | } \ |
| 413 | break; \ |
| 414 | case 0x09 : \ |
| 415 | _v = (_v - 0x90) * 0x100 + *_p++; \ |
| 416 | _v = _v * 0x100 + *_p++; \ |
| 417 | _v = _v * 0x100 + *_p++ + 0x20408f; \ |
| 418 | break; \ |
| 419 | case 0x0a : \ |
| 420 | case 0x0b : \ |
| 421 | _v = (_v - 0xa0) * 0x100 + *_p++; \ |
| 422 | _v = _v * 0x100 + *_p++ + 0x408f; \ |
| 423 | break; \ |
| 424 | case 0x0c : \ |
| 425 | case 0x0d : \ |
| 426 | case 0x0e : \ |
| 427 | case 0x0f : \ |
| 428 | _v = (_v - 0xc0) * 0x100 + *_p++ + 0x8f; \ |
| 429 | break; \ |
| 430 | } \ |
| 431 | v = _v; \ |
| 432 | p = _p; \ |
| 433 | } while (0) |
| 434 | |
| 435 | #define GRN_B_SKIP(p) do { \ |
| 436 | uint8_t *_p = (uint8_t *)p; \ |
| 437 | uint32_t _v = *_p++; \ |
| 438 | switch (_v >> 4) { \ |
| 439 | case 0x08 : \ |
| 440 | if (_v == 0x8f) { \ |
| 441 | _p += sizeof(uint32_t); \ |
| 442 | } \ |
| 443 | break; \ |
| 444 | case 0x09 : \ |
| 445 | _p += 3; \ |
| 446 | break; \ |
| 447 | case 0x0a : \ |
| 448 | case 0x0b : \ |
| 449 | _p += 2; \ |
| 450 | break; \ |
| 451 | case 0x0c : \ |
| 452 | case 0x0d : \ |
| 453 | case 0x0e : \ |
| 454 | case 0x0f : \ |
| 455 | _p += 1; \ |
| 456 | break; \ |
| 457 | } \ |
| 458 | p = _p; \ |
| 459 | } while (0) |
| 460 | |
| 461 | #define GRN_B_COPY(p2,p1) do { \ |
| 462 | uint32_t size = 0, _v = *p1++; \ |
| 463 | *p2++ = _v; \ |
| 464 | switch (_v >> 4) { \ |
| 465 | case 0x08 : \ |
| 466 | size = (_v == 0x8f) ? 4 : 0; \ |
| 467 | break; \ |
| 468 | case 0x09 : \ |
| 469 | size = 3; \ |
| 470 | break; \ |
| 471 | case 0x0a : \ |
| 472 | case 0x0b : \ |
| 473 | size = 2; \ |
| 474 | break; \ |
| 475 | case 0x0c : \ |
| 476 | case 0x0d : \ |
| 477 | case 0x0e : \ |
| 478 | case 0x0f : \ |
| 479 | size = 1; \ |
| 480 | break; \ |
| 481 | } \ |
| 482 | while (size--) { *p2++ = *p1++; } \ |
| 483 | } while (0) |
| 484 | |
| 485 | #ifdef __cplusplus |
| 486 | } |
| 487 | #endif |
| 488 | |