1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ****************************************************************************** 5 * 6 * Copyright (C) 1997-2016, International Business Machines 7 * Corporation and others. All Rights Reserved. 8 * 9 ****************************************************************************** 10 * 11 * File CMEMORY.H 12 * 13 * Contains stdlib.h/string.h memory functions 14 * 15 * @author Bertrand A. Damiba 16 * 17 * Modification History: 18 * 19 * Date Name Description 20 * 6/20/98 Bertrand Created. 21 * 05/03/99 stephen Changed from functions to macros. 22 * 23 ****************************************************************************** 24 */ 25 26 #ifndef CMEMORY_H 27 #define CMEMORY_H 28 29 #include "unicode/utypes.h" 30 31 #include <stddef.h> 32 #include <string.h> 33 #include "unicode/localpointer.h" 34 35 #if U_DEBUG && defined(UPRV_MALLOC_COUNT) 36 #include <stdio.h> 37 #endif 38 39 #if U_DEBUG 40 41 /* 42 * The C++ standard requires that the source pointer for memcpy() & memmove() 43 * is valid, not NULL, and not at the end of an allocated memory block. 44 * In debug mode, we read one byte from the source point to verify that it's 45 * a valid, readable pointer. 46 */ 47 48 U_CAPI void uprv_checkValidMemory(const void *p, size_t n); 49 50 #define uprv_memcpy(dst, src, size) ( \ 51 uprv_checkValidMemory(src, 1), \ 52 U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size)) 53 #define uprv_memmove(dst, src, size) ( \ 54 uprv_checkValidMemory(src, 1), \ 55 U_STANDARD_CPP_NAMESPACE memmove(dst, src, size)) 56 57 #else 58 59 #define uprv_memcpy(dst, src, size) U_STANDARD_CPP_NAMESPACE memcpy(dst, src, size) 60 #define uprv_memmove(dst, src, size) U_STANDARD_CPP_NAMESPACE memmove(dst, src, size) 61 62 #endif /* U_DEBUG */ 63 64 /** 65 * \def UPRV_LENGTHOF 66 * Convenience macro to determine the length of a fixed array at compile-time. 67 * @param array A fixed length array 68 * @return The length of the array, in elements 69 * @internal 70 */ 71 #define UPRV_LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) 72 #define uprv_memset(buffer, mark, size) U_STANDARD_CPP_NAMESPACE memset(buffer, mark, size) 73 #define uprv_memcmp(buffer1, buffer2, size) U_STANDARD_CPP_NAMESPACE memcmp(buffer1, buffer2,size) 74 75 U_CAPI void * U_EXPORT2 76 uprv_malloc(size_t s) U_MALLOC_ATTR U_ALLOC_SIZE_ATTR(1); 77 78 U_CAPI void * U_EXPORT2 79 uprv_realloc(void *mem, size_t size) U_ALLOC_SIZE_ATTR(2); 80 81 U_CAPI void U_EXPORT2 82 uprv_free(void *mem); 83 84 U_CAPI void * U_EXPORT2 85 uprv_calloc(size_t num, size_t size) U_MALLOC_ATTR U_ALLOC_SIZE_ATTR2(1,2); 86 87 /** 88 * This should align the memory properly on any machine. 89 * This is very useful for the safeClone functions. 90 */ 91 typedef union { 92 long t1; 93 double t2; 94 void *t3; 95 } UAlignedMemory; 96 97 /** 98 * Get the least significant bits of a pointer (a memory address). 99 * For example, with a mask of 3, the macro gets the 2 least significant bits, 100 * which will be 0 if the pointer is 32-bit (4-byte) aligned. 101 * 102 * ptrdiff_t is the most appropriate integer type to cast to. 103 * size_t should work too, since on most (or all?) platforms it has the same 104 * width as ptrdiff_t. 105 */ 106 #define U_POINTER_MASK_LSB(ptr, mask) (((ptrdiff_t)(char *)(ptr)) & (mask)) 107 108 /** 109 * Get the amount of bytes that a pointer is off by from 110 * the previous UAlignedMemory-aligned pointer. 111 */ 112 #define U_ALIGNMENT_OFFSET(ptr) U_POINTER_MASK_LSB(ptr, sizeof(UAlignedMemory) - 1) 113 114 /** 115 * Get the amount of bytes to add to a pointer 116 * in order to get the next UAlignedMemory-aligned address. 117 */ 118 #define U_ALIGNMENT_OFFSET_UP(ptr) (sizeof(UAlignedMemory) - U_ALIGNMENT_OFFSET(ptr)) 119 120 /** 121 * Heap clean up function, called from u_cleanup() 122 * Clears any user heap functions from u_setMemoryFunctions() 123 * Does NOT deallocate any remaining allocated memory. 124 */ 125 U_CFUNC UBool 126 cmemory_cleanup(void); 127 128 /** 129 * A function called by <TT>uhash_remove</TT>, 130 * <TT>uhash_close</TT>, or <TT>uhash_put</TT> to delete 131 * an existing key or value. 132 * @param obj A key or value stored in a hashtable 133 * @see uprv_deleteUObject 134 */ 135 typedef void U_CALLCONV UObjectDeleter(void* obj); 136 137 /** 138 * Deleter for UObject instances. 139 * Works for all subclasses of UObject because it has a virtual destructor. 140 */ 141 U_CAPI void U_EXPORT2 142 uprv_deleteUObject(void *obj); 143 144 #ifdef __cplusplus 145 146 U_NAMESPACE_BEGIN 147 148 /** 149 * "Smart pointer" class, deletes memory via uprv_free(). 150 * For most methods see the LocalPointerBase base class. 151 * Adds operator[] for array item access. 152 * 153 * @see LocalPointerBase 154 */ 155 template<typename T> 156 class LocalMemory : public LocalPointerBase<T> { 157 public: 158 using LocalPointerBase<T>::operator*; 159 using LocalPointerBase<T>::operator->; 160 /** 161 * Constructor takes ownership. 162 * @param p simple pointer to an array of T items that is adopted 163 */ 164 explicit LocalMemory(T *p=NULL) : LocalPointerBase<T>(p) {} 165 /** 166 * Move constructor, leaves src with isNull(). 167 * @param src source smart pointer 168 */ 169 LocalMemory(LocalMemory<T> &&src) U_NOEXCEPT : LocalPointerBase<T>(src.ptr) { 170 src.ptr=NULL; 171 } 172 /** 173 * Destructor deletes the memory it owns. 174 */ 175 ~LocalMemory() { 176 uprv_free(LocalPointerBase<T>::ptr); 177 } 178 /** 179 * Move assignment operator, leaves src with isNull(). 180 * The behavior is undefined if *this and src are the same object. 181 * @param src source smart pointer 182 * @return *this 183 */ 184 LocalMemory<T> &operator=(LocalMemory<T> &&src) U_NOEXCEPT { 185 return moveFrom(src); 186 } 187 /** 188 * Move assignment, leaves src with isNull(). 189 * The behavior is undefined if *this and src are the same object. 190 * 191 * Can be called explicitly, does not need C++11 support. 192 * @param src source smart pointer 193 * @return *this 194 */ 195 LocalMemory<T> &moveFrom(LocalMemory<T> &src) U_NOEXCEPT { 196 delete[] LocalPointerBase<T>::ptr; 197 LocalPointerBase<T>::ptr=src.ptr; 198 src.ptr=NULL; 199 return *this; 200 } 201 /** 202 * Swap pointers. 203 * @param other other smart pointer 204 */ 205 void swap(LocalMemory<T> &other) U_NOEXCEPT { 206 T *temp=LocalPointerBase<T>::ptr; 207 LocalPointerBase<T>::ptr=other.ptr; 208 other.ptr=temp; 209 } 210 /** 211 * Non-member LocalMemory swap function. 212 * @param p1 will get p2's pointer 213 * @param p2 will get p1's pointer 214 */ 215 friend inline void swap(LocalMemory<T> &p1, LocalMemory<T> &p2) U_NOEXCEPT { 216 p1.swap(p2); 217 } 218 /** 219 * Deletes the array it owns, 220 * and adopts (takes ownership of) the one passed in. 221 * @param p simple pointer to an array of T items that is adopted 222 */ 223 void adoptInstead(T *p) { 224 uprv_free(LocalPointerBase<T>::ptr); 225 LocalPointerBase<T>::ptr=p; 226 } 227 /** 228 * Deletes the array it owns, allocates a new one and reset its bytes to 0. 229 * Returns the new array pointer. 230 * If the allocation fails, then the current array is unchanged and 231 * this method returns NULL. 232 * @param newCapacity must be >0 233 * @return the allocated array pointer, or NULL if the allocation failed 234 */ 235 inline T *allocateInsteadAndReset(int32_t newCapacity=1); 236 /** 237 * Deletes the array it owns and allocates a new one, copying length T items. 238 * Returns the new array pointer. 239 * If the allocation fails, then the current array is unchanged and 240 * this method returns NULL. 241 * @param newCapacity must be >0 242 * @param length number of T items to be copied from the old array to the new one; 243 * must be no more than the capacity of the old array, 244 * which the caller must track because the LocalMemory does not track it 245 * @return the allocated array pointer, or NULL if the allocation failed 246 */ 247 inline T *allocateInsteadAndCopy(int32_t newCapacity=1, int32_t length=0); 248 /** 249 * Array item access (writable). 250 * No index bounds check. 251 * @param i array index 252 * @return reference to the array item 253 */ 254 T &operator[](ptrdiff_t i) const { return LocalPointerBase<T>::ptr[i]; } 255 }; 256 257 template<typename T> 258 inline T *LocalMemory<T>::allocateInsteadAndReset(int32_t newCapacity) { 259 if(newCapacity>0) { 260 T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); 261 if(p!=NULL) { 262 uprv_memset(p, 0, newCapacity*sizeof(T)); 263 uprv_free(LocalPointerBase<T>::ptr); 264 LocalPointerBase<T>::ptr=p; 265 } 266 return p; 267 } else { 268 return NULL; 269 } 270 } 271 272 273 template<typename T> 274 inline T *LocalMemory<T>::allocateInsteadAndCopy(int32_t newCapacity, int32_t length) { 275 if(newCapacity>0) { 276 T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); 277 if(p!=NULL) { 278 if(length>0) { 279 if(length>newCapacity) { 280 length=newCapacity; 281 } 282 uprv_memcpy(p, LocalPointerBase<T>::ptr, (size_t)length*sizeof(T)); 283 } 284 uprv_free(LocalPointerBase<T>::ptr); 285 LocalPointerBase<T>::ptr=p; 286 } 287 return p; 288 } else { 289 return NULL; 290 } 291 } 292 293 /** 294 * Simple array/buffer management class using uprv_malloc() and uprv_free(). 295 * Provides an internal array with fixed capacity. Can alias another array 296 * or allocate one. 297 * 298 * The array address is properly aligned for type T. It might not be properly 299 * aligned for types larger than T (or larger than the largest subtype of T). 300 * 301 * Unlike LocalMemory and LocalArray, this class never adopts 302 * (takes ownership of) another array. 303 */ 304 template<typename T, int32_t stackCapacity> 305 class MaybeStackArray { 306 public: 307 /** 308 * Default constructor initializes with internal T[stackCapacity] buffer. 309 */ 310 MaybeStackArray() : ptr(stackArray), capacity(stackCapacity), needToRelease(FALSE) {} 311 /** 312 * Automatically allocates the heap array if the argument is larger than the stack capacity. 313 * Intended for use when an approximate capacity is known at compile time but the true 314 * capacity is not known until runtime. 315 */ 316 MaybeStackArray(int32_t newCapacity) : MaybeStackArray() { 317 if (capacity < newCapacity) { resize(newCapacity); } 318 }; 319 /** 320 * Destructor deletes the array (if owned). 321 */ 322 ~MaybeStackArray() { releaseArray(); } 323 /** 324 * Returns the array capacity (number of T items). 325 * @return array capacity 326 */ 327 int32_t getCapacity() const { return capacity; } 328 /** 329 * Access without ownership change. 330 * @return the array pointer 331 */ 332 T *getAlias() const { return ptr; } 333 /** 334 * Returns the array limit. Simple convenience method. 335 * @return getAlias()+getCapacity() 336 */ 337 T *getArrayLimit() const { return getAlias()+capacity; } 338 // No "operator T *() const" because that can make 339 // expressions like mbs[index] ambiguous for some compilers. 340 /** 341 * Array item access (const). 342 * No index bounds check. 343 * @param i array index 344 * @return reference to the array item 345 */ 346 const T &operator[](ptrdiff_t i) const { return ptr[i]; } 347 /** 348 * Array item access (writable). 349 * No index bounds check. 350 * @param i array index 351 * @return reference to the array item 352 */ 353 T &operator[](ptrdiff_t i) { return ptr[i]; } 354 /** 355 * Deletes the array (if owned) and aliases another one, no transfer of ownership. 356 * If the arguments are illegal, then the current array is unchanged. 357 * @param otherArray must not be NULL 358 * @param otherCapacity must be >0 359 */ 360 void aliasInstead(T *otherArray, int32_t otherCapacity) { 361 if(otherArray!=NULL && otherCapacity>0) { 362 releaseArray(); 363 ptr=otherArray; 364 capacity=otherCapacity; 365 needToRelease=FALSE; 366 } 367 } 368 /** 369 * Deletes the array (if owned) and allocates a new one, copying length T items. 370 * Returns the new array pointer. 371 * If the allocation fails, then the current array is unchanged and 372 * this method returns NULL. 373 * @param newCapacity can be less than or greater than the current capacity; 374 * must be >0 375 * @param length number of T items to be copied from the old array to the new one 376 * @return the allocated array pointer, or NULL if the allocation failed 377 */ 378 inline T *resize(int32_t newCapacity, int32_t length=0); 379 /** 380 * Gives up ownership of the array if owned, or else clones it, 381 * copying length T items; resets itself to the internal stack array. 382 * Returns NULL if the allocation failed. 383 * @param length number of T items to copy when cloning, 384 * and capacity of the clone when cloning 385 * @param resultCapacity will be set to the returned array's capacity (output-only) 386 * @return the array pointer; 387 * caller becomes responsible for deleting the array 388 */ 389 inline T *orphanOrClone(int32_t length, int32_t &resultCapacity); 390 private: 391 T *ptr; 392 int32_t capacity; 393 UBool needToRelease; 394 T stackArray[stackCapacity]; 395 void releaseArray() { 396 if(needToRelease) { 397 uprv_free(ptr); 398 } 399 } 400 /* No comparison operators with other MaybeStackArray's. */ 401 bool operator==(const MaybeStackArray & /*other*/) {return FALSE;} 402 bool operator!=(const MaybeStackArray & /*other*/) {return TRUE;} 403 /* No ownership transfer: No copy constructor, no assignment operator. */ 404 MaybeStackArray(const MaybeStackArray & /*other*/) {} 405 void operator=(const MaybeStackArray & /*other*/) {} 406 407 // No heap allocation. Use only on the stack. 408 // (Declaring these functions private triggers a cascade of problems: 409 // MSVC insists on exporting an instantiation of MaybeStackArray, which 410 // requires that all functions be defined. 411 // An empty implementation of new() is rejected, it must return a value. 412 // Returning NULL is rejected by gcc for operator new. 413 // The expedient thing is just not to override operator new. 414 // While relatively pointless, heap allocated instances will function. 415 // static void * U_EXPORT2 operator new(size_t size); 416 // static void * U_EXPORT2 operator new[](size_t size); 417 #if U_HAVE_PLACEMENT_NEW 418 // static void * U_EXPORT2 operator new(size_t, void *ptr); 419 #endif 420 }; 421 422 template<typename T, int32_t stackCapacity> 423 inline T *MaybeStackArray<T, stackCapacity>::resize(int32_t newCapacity, int32_t length) { 424 if(newCapacity>0) { 425 #if U_DEBUG && defined(UPRV_MALLOC_COUNT) 426 ::fprintf(::stderr,"MaybeStacArray (resize) alloc %d * %lu\n", newCapacity,sizeof(T)); 427 #endif 428 T *p=(T *)uprv_malloc(newCapacity*sizeof(T)); 429 if(p!=NULL) { 430 if(length>0) { 431 if(length>capacity) { 432 length=capacity; 433 } 434 if(length>newCapacity) { 435 length=newCapacity; 436 } 437 uprv_memcpy(p, ptr, (size_t)length*sizeof(T)); 438 } 439 releaseArray(); 440 ptr=p; 441 capacity=newCapacity; 442 needToRelease=TRUE; 443 } 444 return p; 445 } else { 446 return NULL; 447 } 448 } 449 450 template<typename T, int32_t stackCapacity> 451 inline T *MaybeStackArray<T, stackCapacity>::orphanOrClone(int32_t length, int32_t &resultCapacity) { 452 T *p; 453 if(needToRelease) { 454 p=ptr; 455 } else if(length<=0) { 456 return NULL; 457 } else { 458 if(length>capacity) { 459 length=capacity; 460 } 461 p=(T *)uprv_malloc(length*sizeof(T)); 462 #if U_DEBUG && defined(UPRV_MALLOC_COUNT) 463 ::fprintf(::stderr,"MaybeStacArray (orphan) alloc %d * %lu\n", length,sizeof(T)); 464 #endif 465 if(p==NULL) { 466 return NULL; 467 } 468 uprv_memcpy(p, ptr, (size_t)length*sizeof(T)); 469 } 470 resultCapacity=length; 471 ptr=stackArray; 472 capacity=stackCapacity; 473 needToRelease=FALSE; 474 return p; 475 } 476 477 /** 478 * Variant of MaybeStackArray that allocates a header struct and an array 479 * in one contiguous memory block, using uprv_malloc() and uprv_free(). 480 * Provides internal memory with fixed array capacity. Can alias another memory 481 * block or allocate one. 482 * The stackCapacity is the number of T items in the internal memory, 483 * not counting the H header. 484 * Unlike LocalMemory and LocalArray, this class never adopts 485 * (takes ownership of) another memory block. 486 */ 487 template<typename H, typename T, int32_t stackCapacity> 488 class MaybeStackHeaderAndArray { 489 public: 490 /** 491 * Default constructor initializes with internal H+T[stackCapacity] buffer. 492 */ 493 MaybeStackHeaderAndArray() : ptr(&stackHeader), capacity(stackCapacity), needToRelease(FALSE) {} 494 /** 495 * Destructor deletes the memory (if owned). 496 */ 497 ~MaybeStackHeaderAndArray() { releaseMemory(); } 498 /** 499 * Returns the array capacity (number of T items). 500 * @return array capacity 501 */ 502 int32_t getCapacity() const { return capacity; } 503 /** 504 * Access without ownership change. 505 * @return the header pointer 506 */ 507 H *getAlias() const { return ptr; } 508 /** 509 * Returns the array start. 510 * @return array start, same address as getAlias()+1 511 */ 512 T *getArrayStart() const { return reinterpret_cast<T *>(getAlias()+1); } 513 /** 514 * Returns the array limit. 515 * @return array limit 516 */ 517 T *getArrayLimit() const { return getArrayStart()+capacity; } 518 /** 519 * Access without ownership change. Same as getAlias(). 520 * A class instance can be used directly in expressions that take a T *. 521 * @return the header pointer 522 */ 523 operator H *() const { return ptr; } 524 /** 525 * Array item access (writable). 526 * No index bounds check. 527 * @param i array index 528 * @return reference to the array item 529 */ 530 T &operator[](ptrdiff_t i) { return getArrayStart()[i]; } 531 /** 532 * Deletes the memory block (if owned) and aliases another one, no transfer of ownership. 533 * If the arguments are illegal, then the current memory is unchanged. 534 * @param otherArray must not be NULL 535 * @param otherCapacity must be >0 536 */ 537 void aliasInstead(H *otherMemory, int32_t otherCapacity) { 538 if(otherMemory!=NULL && otherCapacity>0) { 539 releaseMemory(); 540 ptr=otherMemory; 541 capacity=otherCapacity; 542 needToRelease=FALSE; 543 } 544 } 545 /** 546 * Deletes the memory block (if owned) and allocates a new one, 547 * copying the header and length T array items. 548 * Returns the new header pointer. 549 * If the allocation fails, then the current memory is unchanged and 550 * this method returns NULL. 551 * @param newCapacity can be less than or greater than the current capacity; 552 * must be >0 553 * @param length number of T items to be copied from the old array to the new one 554 * @return the allocated pointer, or NULL if the allocation failed 555 */ 556 inline H *resize(int32_t newCapacity, int32_t length=0); 557 /** 558 * Gives up ownership of the memory if owned, or else clones it, 559 * copying the header and length T array items; resets itself to the internal memory. 560 * Returns NULL if the allocation failed. 561 * @param length number of T items to copy when cloning, 562 * and array capacity of the clone when cloning 563 * @param resultCapacity will be set to the returned array's capacity (output-only) 564 * @return the header pointer; 565 * caller becomes responsible for deleting the array 566 */ 567 inline H *orphanOrClone(int32_t length, int32_t &resultCapacity); 568 private: 569 H *ptr; 570 int32_t capacity; 571 UBool needToRelease; 572 // stackHeader must precede stackArray immediately. 573 H stackHeader; 574 T stackArray[stackCapacity]; 575 void releaseMemory() { 576 if(needToRelease) { 577 uprv_free(ptr); 578 } 579 } 580 /* No comparison operators with other MaybeStackHeaderAndArray's. */ 581 bool operator==(const MaybeStackHeaderAndArray & /*other*/) {return FALSE;} 582 bool operator!=(const MaybeStackHeaderAndArray & /*other*/) {return TRUE;} 583 /* No ownership transfer: No copy constructor, no assignment operator. */ 584 MaybeStackHeaderAndArray(const MaybeStackHeaderAndArray & /*other*/) {} 585 void operator=(const MaybeStackHeaderAndArray & /*other*/) {} 586 587 // No heap allocation. Use only on the stack. 588 // (Declaring these functions private triggers a cascade of problems; 589 // see the MaybeStackArray class for details.) 590 // static void * U_EXPORT2 operator new(size_t size); 591 // static void * U_EXPORT2 operator new[](size_t size); 592 #if U_HAVE_PLACEMENT_NEW 593 // static void * U_EXPORT2 operator new(size_t, void *ptr); 594 #endif 595 }; 596 597 template<typename H, typename T, int32_t stackCapacity> 598 inline H *MaybeStackHeaderAndArray<H, T, stackCapacity>::resize(int32_t newCapacity, 599 int32_t length) { 600 if(newCapacity>=0) { 601 #if U_DEBUG && defined(UPRV_MALLOC_COUNT) 602 ::fprintf(::stderr,"MaybeStackHeaderAndArray alloc %d + %d * %ul\n", sizeof(H),newCapacity,sizeof(T)); 603 #endif 604 H *p=(H *)uprv_malloc(sizeof(H)+newCapacity*sizeof(T)); 605 if(p!=NULL) { 606 if(length<0) { 607 length=0; 608 } else if(length>0) { 609 if(length>capacity) { 610 length=capacity; 611 } 612 if(length>newCapacity) { 613 length=newCapacity; 614 } 615 } 616 uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T)); 617 releaseMemory(); 618 ptr=p; 619 capacity=newCapacity; 620 needToRelease=TRUE; 621 } 622 return p; 623 } else { 624 return NULL; 625 } 626 } 627 628 template<typename H, typename T, int32_t stackCapacity> 629 inline H *MaybeStackHeaderAndArray<H, T, stackCapacity>::orphanOrClone(int32_t length, 630 int32_t &resultCapacity) { 631 H *p; 632 if(needToRelease) { 633 p=ptr; 634 } else { 635 if(length<0) { 636 length=0; 637 } else if(length>capacity) { 638 length=capacity; 639 } 640 #if U_DEBUG && defined(UPRV_MALLOC_COUNT) 641 ::fprintf(::stderr,"MaybeStackHeaderAndArray (orphan) alloc %ul + %d * %lu\n", sizeof(H),length,sizeof(T)); 642 #endif 643 p=(H *)uprv_malloc(sizeof(H)+length*sizeof(T)); 644 if(p==NULL) { 645 return NULL; 646 } 647 uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T)); 648 } 649 resultCapacity=length; 650 ptr=&stackHeader; 651 capacity=stackCapacity; 652 needToRelease=FALSE; 653 return p; 654 } 655 656 U_NAMESPACE_END 657 658 #endif /* __cplusplus */ 659 #endif /* CMEMORY_H */ 660