1 /* hash a key 2 *-------------------------------------------------------------------------------------- 3 * The "hash seed" feature was added in Perl 5.8.1 to perturb the results 4 * to avoid "algorithmic complexity attacks". 5 * 6 * If USE_HASH_SEED is defined, hash randomisation is done by default 7 * If USE_HASH_SEED_EXPLICIT is defined, hash randomisation is done 8 * only if the environment variable PERL_HASH_SEED is set. 9 * (see also perl.c:perl_parse() and S_init_tls_and_interp() and util.c:get_hash_seed()) 10 */ 11 12 #ifndef PERL_SEEN_HV_FUNC_H /* compile once */ 13 #define PERL_SEEN_HV_FUNC_H 14 15 #if !( 0 \ 16 || defined(PERL_HASH_FUNC_SIPHASH) \ 17 || defined(PERL_HASH_FUNC_SDBM) \ 18 || defined(PERL_HASH_FUNC_DJB2) \ 19 || defined(PERL_HASH_FUNC_SUPERFAST) \ 20 || defined(PERL_HASH_FUNC_MURMUR3) \ 21 || defined(PERL_HASH_FUNC_ONE_AT_A_TIME) \ 22 || defined(PERL_HASH_FUNC_ONE_AT_A_TIME_HARD) \ 23 || defined(PERL_HASH_FUNC_ONE_AT_A_TIME_OLD) \ 24 ) 25 #define PERL_HASH_FUNC_ONE_AT_A_TIME_HARD 26 #endif 27 28 #if defined(PERL_HASH_FUNC_SIPHASH) 29 # define PERL_HASH_FUNC "SIPHASH_2_4" 30 # define PERL_HASH_SEED_BYTES 16 31 # define PERL_HASH(hash,str,len) (hash)= S_perl_hash_siphash_2_4(PERL_HASH_SEED,(U8*)(str),(len)) 32 #elif defined(PERL_HASH_FUNC_SUPERFAST) 33 # define PERL_HASH_FUNC "SUPERFAST" 34 # define PERL_HASH_SEED_BYTES 4 35 # define PERL_HASH(hash,str,len) (hash)= S_perl_hash_superfast(PERL_HASH_SEED,(U8*)(str),(len)) 36 #elif defined(PERL_HASH_FUNC_MURMUR3) 37 # define PERL_HASH_FUNC "MURMUR3" 38 # define PERL_HASH_SEED_BYTES 4 39 # define PERL_HASH(hash,str,len) (hash)= S_perl_hash_murmur3(PERL_HASH_SEED,(U8*)(str),(len)) 40 #elif defined(PERL_HASH_FUNC_DJB2) 41 # define PERL_HASH_FUNC "DJB2" 42 # define PERL_HASH_SEED_BYTES 4 43 # define PERL_HASH(hash,str,len) (hash)= S_perl_hash_djb2(PERL_HASH_SEED,(U8*)(str),(len)) 44 #elif defined(PERL_HASH_FUNC_SDBM) 45 # define PERL_HASH_FUNC "SDBM" 46 # define PERL_HASH_SEED_BYTES 4 47 # define PERL_HASH(hash,str,len) (hash)= S_perl_hash_sdbm(PERL_HASH_SEED,(U8*)(str),(len)) 48 #elif defined(PERL_HASH_FUNC_ONE_AT_A_TIME_HARD) 49 # define PERL_HASH_FUNC "ONE_AT_A_TIME_HARD" 50 # define PERL_HASH_SEED_BYTES 8 51 # define PERL_HASH(hash,str,len) (hash)= S_perl_hash_one_at_a_time_hard(PERL_HASH_SEED,(U8*)(str),(len)) 52 #elif defined(PERL_HASH_FUNC_ONE_AT_A_TIME) 53 # define PERL_HASH_FUNC "ONE_AT_A_TIME" 54 # define PERL_HASH_SEED_BYTES 4 55 # define PERL_HASH(hash,str,len) (hash)= S_perl_hash_one_at_a_time(PERL_HASH_SEED,(U8*)(str),(len)) 56 #elif defined(PERL_HASH_FUNC_ONE_AT_A_TIME_OLD) 57 # define PERL_HASH_FUNC "ONE_AT_A_TIME_OLD" 58 # define PERL_HASH_SEED_BYTES 4 59 # define PERL_HASH(hash,str,len) (hash)= S_perl_hash_old_one_at_a_time(PERL_HASH_SEED,(U8*)(str),(len)) 60 #endif 61 62 #ifndef PERL_HASH 63 #error "No hash function defined!" 64 #endif 65 #ifndef PERL_HASH_SEED_BYTES 66 #error "PERL_HASH_SEED_BYTES not defined" 67 #endif 68 #ifndef PERL_HASH_FUNC 69 #error "PERL_HASH_FUNC not defined" 70 #endif 71 72 #ifndef PERL_HASH_SEED 73 # if defined(USE_HASH_SEED) || defined(USE_HASH_SEED_EXPLICIT) 74 # define PERL_HASH_SEED PL_hash_seed 75 # elif PERL_HASH_SEED_BYTES == 4 76 # define PERL_HASH_SEED "PeRl" 77 # elif PERL_HASH_SEED_BYTES == 16 78 # define PERL_HASH_SEED "PeRlHaShhAcKpErl" 79 # else 80 # error "No PERL_HASH_SEED definition for " PERL_HASH_FUNC 81 # endif 82 #endif 83 84 /*----------------------------------------------------------------------------- 85 * Endianess, misalignment capabilities and util macros 86 * 87 * The following 3 macros are defined in this section. The other macros defined 88 * are only needed to help derive these 3. 89 * 90 * U8TO32_LE(x) Read a little endian unsigned 32-bit int 91 * UNALIGNED_SAFE Defined if READ_UINT32 works on non-word boundaries 92 * ROTL32(x,r) Rotate x left by r bits 93 */ 94 95 #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ 96 || defined(_MSC_VER) || defined (__TURBOC__) 97 #define U8TO16_LE(d) (*((const U16 *) (d))) 98 #endif 99 100 #if !defined (U8TO16_LE) 101 #define U8TO16_LE(d) ((((const U8 *)(d))[1] << 8)\ 102 +((const U8 *)(d))[0]) 103 #endif 104 105 106 /* Now find best way we can to READ_UINT32 */ 107 #if (BYTEORDER == 0x1234 || BYTEORDER == 0x12345678) && U32SIZE == 4 108 /* CPU endian matches murmurhash algorithm, so read 32-bit word directly */ 109 #define U8TO32_LE(ptr) (*((U32*)(ptr))) 110 #elif BYTEORDER == 0x4321 || BYTEORDER == 0x87654321 111 /* TODO: Add additional cases below where a compiler provided bswap32 is available */ 112 #if defined(__GNUC__) && (__GNUC__>4 || (__GNUC__==4 && __GNUC_MINOR__>=3)) 113 #define U8TO32_LE(ptr) (__builtin_bswap32(*((U32*)(ptr)))) 114 #else 115 /* Without a known fast bswap32 we're just as well off doing this */ 116 #define U8TO32_LE(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24) 117 #define UNALIGNED_SAFE 118 #endif 119 #else 120 /* Unknown endianess so last resort is to read individual bytes */ 121 #define U8TO32_LE(ptr) (ptr[0]|ptr[1]<<8|ptr[2]<<16|ptr[3]<<24) 122 /* Since we're not doing word-reads we can skip the messing about with realignment */ 123 #define UNALIGNED_SAFE 124 #endif 125 126 #ifdef HAS_QUAD 127 #ifndef U64TYPE 128 /* This probably isn't going to work, but failing with a compiler error due to 129 lack of uint64_t is no worse than failing right now with an #error. */ 130 #define U64TYPE uint64_t 131 #endif 132 #endif 133 134 /* Find best way to ROTL32/ROTL64 */ 135 #if defined(_MSC_VER) 136 #include <stdlib.h> /* Microsoft put _rotl declaration in here */ 137 #define ROTL32(x,r) _rotl(x,r) 138 #ifdef HAS_QUAD 139 #define ROTL64(x,r) _rotl64(x,r) 140 #endif 141 #else 142 /* gcc recognises this code and generates a rotate instruction for CPUs with one */ 143 #define ROTL32(x,r) (((U32)x << r) | ((U32)x >> (32 - r))) 144 #ifdef HAS_QUAD 145 #define ROTL64(x,r) (((U64TYPE)x << r) | ((U64TYPE)x >> (64 - r))) 146 #endif 147 #endif 148 149 150 #ifdef UV_IS_QUAD 151 #define ROTL_UV(x,r) ROTL64(x,r) 152 #else 153 #define ROTL_UV(x,r) ROTL32(x,r) 154 #endif 155 156 /* This is SipHash by Jean-Philippe Aumasson and Daniel J. Bernstein. 157 * The authors claim it is relatively secure compared to the alternatives 158 * and that performance wise it is a suitable hash for languages like Perl. 159 * See: 160 * 161 * https://www.131002.net/siphash/ 162 * 163 * This implementation seems to perform slightly slower than one-at-a-time for 164 * short keys, but degrades slower for longer keys. Murmur Hash outperforms it 165 * regardless of keys size. 166 * 167 * It is 64 bit only. 168 */ 169 170 #ifdef HAS_QUAD 171 172 #define U8TO64_LE(p) \ 173 (((U64TYPE)((p)[0]) ) | \ 174 ((U64TYPE)((p)[1]) << 8) | \ 175 ((U64TYPE)((p)[2]) << 16) | \ 176 ((U64TYPE)((p)[3]) << 24) | \ 177 ((U64TYPE)((p)[4]) << 32) | \ 178 ((U64TYPE)((p)[5]) << 40) | \ 179 ((U64TYPE)((p)[6]) << 48) | \ 180 ((U64TYPE)((p)[7]) << 56)) 181 182 #define SIPROUND \ 183 do { \ 184 v0 += v1; v1=ROTL64(v1,13); v1 ^= v0; v0=ROTL64(v0,32); \ 185 v2 += v3; v3=ROTL64(v3,16); v3 ^= v2; \ 186 v0 += v3; v3=ROTL64(v3,21); v3 ^= v0; \ 187 v2 += v1; v1=ROTL64(v1,17); v1 ^= v2; v2=ROTL64(v2,32); \ 188 } while(0) 189 190 /* SipHash-2-4 */ 191 192 PERL_STATIC_INLINE U32 193 S_perl_hash_siphash_2_4(const unsigned char * const seed, const unsigned char *in, const STRLEN inlen) { 194 /* "somepseudorandomlygeneratedbytes" */ 195 U64TYPE v0 = 0x736f6d6570736575ULL; 196 U64TYPE v1 = 0x646f72616e646f6dULL; 197 U64TYPE v2 = 0x6c7967656e657261ULL; 198 U64TYPE v3 = 0x7465646279746573ULL; 199 200 U64TYPE b; 201 U64TYPE k0 = ((U64TYPE*)seed)[0]; 202 U64TYPE k1 = ((U64TYPE*)seed)[1]; 203 U64TYPE m; 204 const int left = inlen & 7; 205 const U8 *end = in + inlen - left; 206 207 b = ( ( U64TYPE )(inlen) ) << 56; 208 v3 ^= k1; 209 v2 ^= k0; 210 v1 ^= k1; 211 v0 ^= k0; 212 213 for ( ; in != end; in += 8 ) 214 { 215 m = U8TO64_LE( in ); 216 v3 ^= m; 217 SIPROUND; 218 SIPROUND; 219 v0 ^= m; 220 } 221 222 switch( left ) 223 { 224 case 7: b |= ( ( U64TYPE )in[ 6] ) << 48; 225 case 6: b |= ( ( U64TYPE )in[ 5] ) << 40; 226 case 5: b |= ( ( U64TYPE )in[ 4] ) << 32; 227 case 4: b |= ( ( U64TYPE )in[ 3] ) << 24; 228 case 3: b |= ( ( U64TYPE )in[ 2] ) << 16; 229 case 2: b |= ( ( U64TYPE )in[ 1] ) << 8; 230 case 1: b |= ( ( U64TYPE )in[ 0] ); break; 231 case 0: break; 232 } 233 234 v3 ^= b; 235 SIPROUND; 236 SIPROUND; 237 v0 ^= b; 238 239 v2 ^= 0xff; 240 SIPROUND; 241 SIPROUND; 242 SIPROUND; 243 SIPROUND; 244 b = v0 ^ v1 ^ v2 ^ v3; 245 return (U32)(b & U32_MAX); 246 } 247 #endif /* defined(HAS_QUAD) */ 248 249 /* FYI: This is the "Super-Fast" algorithm mentioned by Bob Jenkins in 250 * (http://burtleburtle.net/bob/hash/doobs.html) 251 * It is by Paul Hsieh (c) 2004 and is analysed here 252 * http://www.azillionmonkeys.com/qed/hash.html 253 * license terms are here: 254 * http://www.azillionmonkeys.com/qed/weblicense.html 255 */ 256 257 258 PERL_STATIC_INLINE U32 259 S_perl_hash_superfast(const unsigned char * const seed, const unsigned char *str, STRLEN len) { 260 U32 hash = *((U32*)seed) + (U32)len; 261 U32 tmp; 262 int rem= len & 3; 263 len >>= 2; 264 265 for (;len > 0; len--) { 266 hash += U8TO16_LE (str); 267 tmp = (U8TO16_LE (str+2) << 11) ^ hash; 268 hash = (hash << 16) ^ tmp; 269 str += 2 * sizeof (U16); 270 hash += hash >> 11; 271 } 272 273 /* Handle end cases */ 274 switch (rem) { \ 275 case 3: hash += U8TO16_LE (str); 276 hash ^= hash << 16; 277 hash ^= str[sizeof (U16)] << 18; 278 hash += hash >> 11; 279 break; 280 case 2: hash += U8TO16_LE (str); 281 hash ^= hash << 11; 282 hash += hash >> 17; 283 break; 284 case 1: hash += *str; 285 hash ^= hash << 10; 286 hash += hash >> 1; 287 } 288 /* Force "avalanching" of final 127 bits */ 289 hash ^= hash << 3; 290 hash += hash >> 5; 291 hash ^= hash << 4; 292 hash += hash >> 17; 293 hash ^= hash << 25; 294 return (hash + (hash >> 6)); 295 } 296 297 298 /*----------------------------------------------------------------------------- 299 * MurmurHash3 was written by Austin Appleby, and is placed in the public 300 * domain. 301 * 302 * This implementation was originally written by Shane Day, and is also public domain, 303 * and was modified to function as a macro similar to other perl hash functions by 304 * Yves Orton. 305 * 306 * This is a portable ANSI C implementation of MurmurHash3_x86_32 (Murmur3A) 307 * with support for progressive processing. 308 * 309 * If you want to understand the MurmurHash algorithm you would be much better 310 * off reading the original source. Just point your browser at: 311 * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp 312 * 313 * How does it work? 314 * 315 * We can only process entire 32 bit chunks of input, except for the very end 316 * that may be shorter. 317 * 318 * To handle endianess I simply use a macro that reads a U32 and define 319 * that macro to be a direct read on little endian machines, a read and swap 320 * on big endian machines, or a byte-by-byte read if the endianess is unknown. 321 */ 322 323 324 /*----------------------------------------------------------------------------- 325 * Core murmurhash algorithm macros */ 326 327 #define MURMUR_C1 (0xcc9e2d51) 328 #define MURMUR_C2 (0x1b873593) 329 #define MURMUR_C3 (0xe6546b64) 330 #define MURMUR_C4 (0x85ebca6b) 331 #define MURMUR_C5 (0xc2b2ae35) 332 333 /* This is the main processing body of the algorithm. It operates 334 * on each full 32-bits of input. */ 335 #define MURMUR_DOBLOCK(h1, k1) STMT_START { \ 336 k1 *= MURMUR_C1; \ 337 k1 = ROTL32(k1,15); \ 338 k1 *= MURMUR_C2; \ 339 \ 340 h1 ^= k1; \ 341 h1 = ROTL32(h1,13); \ 342 h1 = h1 * 5 + MURMUR_C3; \ 343 } STMT_END 344 345 346 /* Append unaligned bytes to carry, forcing hash churn if we have 4 bytes */ 347 /* cnt=bytes to process, h1=name of h1 var, c=carry, n=bytes in c, ptr/len=payload */ 348 #define MURMUR_DOBYTES(cnt, h1, c, n, ptr, len) STMT_START { \ 349 int MURMUR_DOBYTES_i = cnt; \ 350 while(MURMUR_DOBYTES_i--) { \ 351 c = c>>8 | *ptr++<<24; \ 352 n++; len--; \ 353 if(n==4) { \ 354 MURMUR_DOBLOCK(h1, c); \ 355 n = 0; \ 356 } \ 357 } \ 358 } STMT_END 359 360 361 /* now we create the hash function */ 362 PERL_STATIC_INLINE U32 363 S_perl_hash_murmur3(const unsigned char * const seed, const unsigned char *ptr, STRLEN len) { 364 U32 h1 = *((U32*)seed); 365 U32 k1; 366 U32 carry = 0; 367 368 const unsigned char *end; 369 int bytes_in_carry = 0; /* bytes in carry */ 370 I32 total_length= (I32)len; 371 372 #if defined(UNALIGNED_SAFE) 373 /* Handle carry: commented out as its only used in incremental mode - it never fires for us 374 int i = (4-n) & 3; 375 if(i && i <= len) { 376 MURMUR_DOBYTES(i, h1, carry, bytes_in_carry, ptr, len); 377 } 378 */ 379 380 /* This CPU handles unaligned word access */ 381 /* Process 32-bit chunks */ 382 end = ptr + len/4*4; 383 for( ; ptr < end ; ptr+=4) { 384 k1 = U8TO32_LE(ptr); 385 MURMUR_DOBLOCK(h1, k1); 386 } 387 #else 388 /* This CPU does not handle unaligned word access */ 389 390 /* Consume enough so that the next data byte is word aligned */ 391 STRLEN i = -PTR2IV(ptr) & 3; 392 if(i && i <= len) { 393 MURMUR_DOBYTES((int)i, h1, carry, bytes_in_carry, ptr, len); 394 } 395 396 /* We're now aligned. Process in aligned blocks. Specialise for each possible carry count */ 397 end = ptr + len/4*4; 398 switch(bytes_in_carry) { /* how many bytes in carry */ 399 case 0: /* c=[----] w=[3210] b=[3210]=w c'=[----] */ 400 for( ; ptr < end ; ptr+=4) { 401 k1 = U8TO32_LE(ptr); 402 MURMUR_DOBLOCK(h1, k1); 403 } 404 break; 405 case 1: /* c=[0---] w=[4321] b=[3210]=c>>24|w<<8 c'=[4---] */ 406 for( ; ptr < end ; ptr+=4) { 407 k1 = carry>>24; 408 carry = U8TO32_LE(ptr); 409 k1 |= carry<<8; 410 MURMUR_DOBLOCK(h1, k1); 411 } 412 break; 413 case 2: /* c=[10--] w=[5432] b=[3210]=c>>16|w<<16 c'=[54--] */ 414 for( ; ptr < end ; ptr+=4) { 415 k1 = carry>>16; 416 carry = U8TO32_LE(ptr); 417 k1 |= carry<<16; 418 MURMUR_DOBLOCK(h1, k1); 419 } 420 break; 421 case 3: /* c=[210-] w=[6543] b=[3210]=c>>8|w<<24 c'=[654-] */ 422 for( ; ptr < end ; ptr+=4) { 423 k1 = carry>>8; 424 carry = U8TO32_LE(ptr); 425 k1 |= carry<<24; 426 MURMUR_DOBLOCK(h1, k1); 427 } 428 } 429 #endif 430 /* Advance over whole 32-bit chunks, possibly leaving 1..3 bytes */ 431 len -= len/4*4; 432 433 /* Append any remaining bytes into carry */ 434 MURMUR_DOBYTES((int)len, h1, carry, bytes_in_carry, ptr, len); 435 436 if (bytes_in_carry) { 437 k1 = carry >> ( 4 - bytes_in_carry ) * 8; 438 k1 *= MURMUR_C1; 439 k1 = ROTL32(k1,15); 440 k1 *= MURMUR_C2; 441 h1 ^= k1; 442 } 443 h1 ^= total_length; 444 445 /* fmix */ 446 h1 ^= h1 >> 16; 447 h1 *= MURMUR_C4; 448 h1 ^= h1 >> 13; 449 h1 *= MURMUR_C5; 450 h1 ^= h1 >> 16; 451 return h1; 452 } 453 454 455 PERL_STATIC_INLINE U32 456 S_perl_hash_djb2(const unsigned char * const seed, const unsigned char *str, const STRLEN len) { 457 const unsigned char * const end = (const unsigned char *)str + len; 458 U32 hash = *((U32*)seed) + (U32)len; 459 while (str < end) { 460 hash = ((hash << 5) + hash) + *str++; 461 } 462 return hash; 463 } 464 465 PERL_STATIC_INLINE U32 466 S_perl_hash_sdbm(const unsigned char * const seed, const unsigned char *str, const STRLEN len) { 467 const unsigned char * const end = (const unsigned char *)str + len; 468 U32 hash = *((U32*)seed) + (U32)len; 469 while (str < end) { 470 hash = (hash << 6) + (hash << 16) - hash + *str++; 471 } 472 return hash; 473 } 474 475 /* - ONE_AT_A_TIME_HARD is the 5.17+ recommend ONE_AT_A_TIME algorithm 476 * - ONE_AT_A_TIME_OLD is the unmodified 5.16 and older algorithm 477 * - ONE_AT_A_TIME is a 5.17+ tweak of ONE_AT_A_TIME_OLD to 478 * prevent strings of only \0 but different lengths from colliding 479 * 480 * Security-wise, from best to worst, 481 * ONE_AT_A_TIME_HARD > ONE_AT_A_TIME > ONE_AT_A_TIME_OLD 482 * There is a big drop-off in security between ONE_AT_A_TIME_HARD and 483 * ONE_AT_A_TIME 484 * */ 485 486 /* This is the "One-at-a-Time" algorithm by Bob Jenkins 487 * from requirements by Colin Plumb. 488 * (http://burtleburtle.net/bob/hash/doobs.html) 489 * With seed/len tweak. 490 * */ 491 PERL_STATIC_INLINE U32 492 S_perl_hash_one_at_a_time(const unsigned char * const seed, const unsigned char *str, const STRLEN len) { 493 const unsigned char * const end = (const unsigned char *)str + len; 494 U32 hash = *((U32*)seed) + (U32)len; 495 while (str < end) { 496 hash += *str++; 497 hash += (hash << 10); 498 hash ^= (hash >> 6); 499 } 500 hash += (hash << 3); 501 hash ^= (hash >> 11); 502 return (hash + (hash << 15)); 503 } 504 505 /* Derived from "One-at-a-Time" algorithm by Bob Jenkins */ 506 PERL_STATIC_INLINE U32 507 S_perl_hash_one_at_a_time_hard(const unsigned char * const seed, const unsigned char *str, const STRLEN len) { 508 const unsigned char * const end = (const unsigned char *)str + len; 509 U32 hash = *((U32*)seed) + (U32)len; 510 511 while (str < end) { 512 hash += (hash << 10); 513 hash ^= (hash >> 6); 514 hash += *str++; 515 } 516 517 hash += (hash << 10); 518 hash ^= (hash >> 6); 519 hash += seed[4]; 520 521 hash += (hash << 10); 522 hash ^= (hash >> 6); 523 hash += seed[5]; 524 525 hash += (hash << 10); 526 hash ^= (hash >> 6); 527 hash += seed[6]; 528 529 hash += (hash << 10); 530 hash ^= (hash >> 6); 531 hash += seed[7]; 532 533 hash += (hash << 10); 534 hash ^= (hash >> 6); 535 536 hash += (hash << 3); 537 hash ^= (hash >> 11); 538 return (hash + (hash << 15)); 539 } 540 541 PERL_STATIC_INLINE U32 542 S_perl_hash_old_one_at_a_time(const unsigned char * const seed, const unsigned char *str, const STRLEN len) { 543 const unsigned char * const end = (const unsigned char *)str + len; 544 U32 hash = *((U32*)seed); 545 while (str < end) { 546 hash += *str++; 547 hash += (hash << 10); 548 hash ^= (hash >> 6); 549 } 550 hash += (hash << 3); 551 hash ^= (hash >> 11); 552 return (hash + (hash << 15)); 553 } 554 555 /* legacy - only mod_perl should be doing this. */ 556 #ifdef PERL_HASH_INTERNAL_ACCESS 557 #define PERL_HASH_INTERNAL(hash,str,len) PERL_HASH(hash,str,len) 558 #endif 559 560 #endif /*compile once*/ 561 562 /* 563 * Local variables: 564 * c-indentation-style: bsd 565 * c-basic-offset: 4 566 * indent-tabs-mode: nil 567 * End: 568 * 569 * ex: set ts=8 sts=4 sw=4 et: 570 */ 571