/* define to activate stats reporting on hash usage*/ /* #define HASH_STAT */
/* =============================================== * Set-up: defines to identify the system and system related properties * ===============================================
*/
/* =============================================== * memory pool for fast fix-size allocation (non-thread-safe) * ===============================================
*/ struct pool
{ void* head_free; /**< head of a linked list of freed element */ char* fresh; /**< top of a memory block to dig new element */ char* tail; /**< to detect end of extent... when fresh pass tail */ void* extent; /**< pointer to the primary extent block */ int size_elem; /**< size of an element. */ int primary; /**< primary allocation in bytes */ int secondary; /**< secondary allocation in bytes */
}; #define POOL_ALIGN_INCREMENT 8 /**< alignment, must be a power of 2 and of size > to sizeof(void*) */
staticvoid* pool_take_extent(struct pool* pool, int allocate)
{ unsignedint size = 0; void* extent; void* data = NULL;
/* Create a memory pool for fix size objects * this is a simplified implementation that * is _not_ thread safe.
*/ staticstruct pool* pool_create(int size_elem, int primary, int secondary)
{ struct pool* pool;
pool = (struct pool*)calloc(1, sizeof(struct pool)); if(!pool) return NULL; /* Adjust the element size so that it be aligned, and so that an element could * at least contain a void*
*/
pool->size_elem = size_elem = (size_elem + POOL_ALIGN_INCREMENT - 1) & ~(POOL_ALIGN_INCREMENT - 1);
data = pool->head_free; if(data == NULL)
{ /* we have no old-freed elem */ if(pool->fresh <= pool->tail)
{ /* pick a slice of the current extent */
data = (void*)pool->fresh;
pool->fresh += pool->size_elem;
} else
{ /* allocate a new extent */
data = pool_take_extent(pool, TRUE);
}
} else
{ /* re-used old freed element by chopping the head of the free list */
pool->head_free = *(void**)data;
}
return data;
}
/* =============================================== * Hash implementation customized to be just tracking * a unique list of string (i.e no data associated * with the key, no need for retrieval, etc... * * This is tuned for the particular use-case we have here * measures in tail_build showed that * we can get north of 4000 distinct values stored in a hash * the collision rate is at worse around 2% * the collision needing an expensive memcmp to resolve * have a rate typically at 1 per 1000 * for tail_build we register 37229 unique key * with a total of 377 extra memcmp needed * which is completely negligible compared to the * number of memcmp required to eliminate duplicate * entry (north of 2.5 millions for tail_build) * ===============================================
*/
struct hash
{ struct hash_elem** array; struct pool* elems_pool; unsignedint used; unsignedint size; unsignedint load_limit; #ifdef HASH_STAT int stored; int collisions; int cost; int memcmp; #endif
};
/* The following hash_compute function was adapted from : * lookup3.c, by Bob Jenkins, May 2006, Public Domain. * * The changes from the original are mostly cosmetic
*/ #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
#define mix(a,b,c) \
{ \
a -= c; a ^= rot(c, 4); c += b; \
b -= a; b ^= rot(a, 6); a += c; \
c -= b; c ^= rot(b, 8); b += a; \
a -= c; a ^= rot(c,16); c += b; \
b -= a; b ^= rot(a,19); a += c; \
c -= b; c ^= rot(b, 4); b += a; \
} #define final(a,b,c) \
{ \
c ^= b; c -= rot(b,14); \
a ^= c; a -= rot(c,11); \
b ^= a; b -= rot(a,25); \
c ^= b; c -= rot(b,16); \
a ^= c; a -= rot(c,4); \
b ^= a; b -= rot(a,14); \
c ^= b; c -= rot(b,24); \
}
staticunsignedint hash_compute( struct hash const * hash, constchar* key, int length)
{ unsignedint a; unsignedint b; unsignedint c; /* internal state */ constunsignedchar* uk = (constunsignedchar*)key;
/* Set up the internal state */
a = b = c = 0xdeadbeef + (length << 2);
/* we use this to 'hash' full path with mostly a common root * let's now waste too much cycles hashing mostly constant stuff
*/ if(length > 36)
{
uk += length - 36;
length = 36;
} /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ while (length > 12)
{
a += get_unaligned_uint(uk);
b += get_unaligned_uint(uk+4);
c += get_unaligned_uint(uk+8);
mix(a,b,c);
length -= 12;
uk += 12;
}
/*----------------------------- handle the last (probably partial) block */ /* Note: we possibly over-read, which would trigger complaint from VALGRIND * but we mask the undefined stuff if any, so we are still good, thanks * to alignment of memory allocation and tail-memory management overhead * we always can read 3 bytes past the official end without triggering * a segfault -- if you find a platform/compiler couple for which that postulate * is false, then you just need to over-allocate by 2 more bytes in file_load() * file_load already over-allocate by 1 to stick a \0 at the end of the buffer.
*/ switch(length)
{ case 12: c+=get_unaligned_uint(uk+8); b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break; case 11: c+=get_unaligned_uint(uk+8) & MASK_C1; b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break; case 10: c+=get_unaligned_uint(uk+8) & MASK_C2; b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break; case 9 : c+=get_unaligned_uint(uk+8) & MASK_C3; b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break; case 8 : b+=get_unaligned_uint(uk+4); a+=get_unaligned_uint(uk); break; case 7 : b+=get_unaligned_uint(uk+4) & MASK_C1; a+=get_unaligned_uint(uk); break; case 6 : b+=get_unaligned_uint(uk+4) & MASK_C2; a+=get_unaligned_uint(uk); break; case 5 : b+=get_unaligned_uint(uk+4) & MASK_C3; a+=get_unaligned_uint(uk); break; case 4 : a+=get_unaligned_uint(uk); break; case 3 : a+=get_unaligned_uint(uk) & MASK_C1; break; case 2 : a+=get_unaligned_uint(uk) & MASK_C2; break; case 1 : a+=get_unaligned_uint(uk) & MASK_C3; break; case 0 : return c & hash->size; /* zero length strings require no mixing */
}
/* a customized hash_store function that just store the key and return * TRUE if the key was effectively stored, or FALSE if the key was already there
*/ staticint hash_store(struct hash* hash, constchar* key, int key_len)
{ unsignedint hashed; struct hash_elem* hash_elem; int cost = 0;
/* * Prune LibreOffice specific duplicate dependencies to improve * gnumake startup time, and shrink the disk-space footprint.
*/ staticint
elide_dependency(constchar* key, int key_len, constchar **unpacked_end)
{ #if 0
{ int i;
fprintf (stderr, "elide?%d!: '", internal_boost); for (i = 0; i < key_len; i++) {
fprintf (stderr, "%c", key[i]);
}
fprintf (stderr, "'\n");
} #endif
/* boost brings a plague of header files */ int i; int unpacked = 0; /* walk down path elements */ for (i = 0; i < key_len - 1; i++)
{ if (key[i] == '/')
{ if (0 == unpacked)
{ if (!PATHNCMP(key + i + 1, "workdir/", 8))
{
unpacked = 1; continue;
}
} else
{ if (!PATHNCMP(key + i + 1, "UnpackedTarball/", 16))
{ if (unpacked_end)
*unpacked_end = strchr(key + i + 17, '/'); return 1;
}
}
}
}
return 0;
}
/* * We collapse tens of internal boost headers to the unpacked target, such * that you can re-compile / install boost and all is well.
*/ staticvoid emit_single_boost_header(void)
{ #define BOOST_TARGET "/UnpackedTarball/boost.done"
fprintf(stdout, "%s" BOOST_TARGET " ", work_dir);
}
/* prefix paths to absolute */ staticvoid print_fullpaths(char* line)
{ char* token; char* end; int boost_count = 0; int token_len; constchar * unpacked_end = NULL; /* end of UnpackedTarget match (if any) */ /* for UnpackedTarget the target is GenC{,xx}Object, don't mangle! */ int target_seen = 0;
token = line;
eat_space(&token); while (*token)
{
end = token; /* hard to believe that in this day and age drive letters still exist */ if (*end && (':' == *(end+1)) &&
(('\\' == *(end+2)) || ('/' == *(end+2))) &&
isalpha((unsignedchar)*end))
{
end = end + 3; /* only one cross, err drive letter per filename */
} while (*end && (' ' != *end) && ('\t' != *end) && (':' != *end)) {
++end;
}
token_len = end - token; if (target_seen &&
elide_dependency(token, token_len, &unpacked_end))
{ if (unpacked_end)
{ if (internal_boost && !PATHNCMP(unpacked_end - 5, "boost", 5))
{
++boost_count; if (boost_count == 1)
emit_single_boost_header(); else
{ /* don't output, and swallow trailing \\\n if any */
token = end;
eat_space(&token); if (token[0] == '\\' && token[1] == '\n')
end = token + 2;
}
} else
{
emit_unpacked_target(token, unpacked_end);
}
unpacked_end = NULL;
}
} else
{ if (fwrite(token, token_len, 1, stdout) != 1)
abort();
fputc(' ', stdout);
}
token = end;
eat_space(&token); if (!target_seen && ':' == *token)
{
target_seen = 1;
fputc(':', stdout);
++token;
eat_space(&token);
}
}
}
staticchar * eat_space_at_end(char * end)
{ char * real_end;
assert('\0' == *end);
real_end = end - 1; while (' ' == *real_end || '\t' == *real_end || '\n' == *real_end
|| ':' == *real_end)
{ /* eat colon and whitespace at end */
--real_end;
} return real_end;
}
buffer = file_load(fn, &size, &rc); if(!rc)
{
base = cursor_out = cursor = end = buffer;
end += size;
/* first eat unneeded space at the beginning of file
*/ while(cursor < end && (*cursor == ' ' || *cursor == '\\'))
++cursor;
while(cursor < end)
{ if(*cursor == '\\')
{
continuation = 1;
*cursor_out++ = *cursor++;
} elseif(*cursor == '/')
{ if(cursor + 2 < end)
{ if(!memcmp(cursor, "/./", 3))
{
cursor += 2; continue;
}
} if(cursor + 3 < end)
{ if(!memcmp(cursor, "/../", 4))
{
cancel_relative(base, &cursor, &cursor_out, end);
}
}
*cursor_out++ = *cursor++;
} elseif(*cursor == '\n')
{ if(!continuation)
{
*cursor_out = 0; if(base < cursor)
{ /* here we have a complete rule */ if(last_ns == ':')
{ /* if the rule ended in ':' that is a no-dep rule * these are the one for which we want to filter * duplicate out
*/ int key_len = eat_space_at_end(cursor_out) - base; if (!elide_dependency(base,key_len + 1, NULL)
&& hash_store(dep_hash, base, key_len))
{ /* DO NOT modify base after it has been added
as key by hash_store */
print_fullpaths(base);
putc('\n', stdout);
}
} else
{ /* rule with dep, just write it */
print_fullpaths(base);
putc('\n', stdout);
}
last_ns = ' '; // cannot hurt to reset it
}
cursor += 1;
base = cursor_out = cursor;
} else
{ /* here we have a '\' followed by \n this is a continuation * i.e not a complete rule yet
*/
*cursor_out++ = *cursor++;
continuation = 0; // cancel current one (empty lines!)
}
} else
{
continuation = 0; /* not using isspace() here save 25% of I refs and 75% of D refs based on cachegrind */ if(*cursor != ' ' && *cursor != '\n' && *cursor != '\t' )
{
last_ns = *cursor;
}
*cursor_out++ = *cursor++;
}
}
/* just in case the file did not end with a \n, there may be a pending rule */ if(base < cursor_out)
{ if(last_ns == ':')
{ int key_len = eat_space_at_end(cursor_out) - base; if (!elide_dependency(base,key_len + 1, NULL) &&
hash_store(dep_hash, base, key_len))
{
puts(base);
putc('\n', stdout);
}
} else
{
puts(base);
putc('\n', stdout);
}
}
} else
{ if(strncmp(fn, work_dir, work_dir_len) == 0)
{ if(strncmp(fn+work_dir_len, "/Dep/", 5) == 0)
{
src_relative = fn+work_dir_len+5; // cases ordered by frequency if(strncmp(src_relative, "CxxObject/", 10) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "GenCxxObject/", 13) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "CObject/", 8) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "GenCObject/", 11) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "SdiObject/", 10) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "AsmObject/", 10) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "ObjCxxObject/", 13) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "ObjCObject/", 11) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "GenObjCxxObject/", 16) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "GenObjCObject/", 14) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "GenAsmObject/", 14) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "GenNasmObject/", 14) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "CxxClrObject/", 13) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} elseif(strncmp(src_relative, "GenCxxClrObject/", 16) == 0)
{
created_line = generate_phony_line(src_relative, "o");
rc = generate_phony_file(fn, created_line);
} else
{
fprintf(stderr, "no magic for %s(%s) in %s\n", fn, src_relative, work_dir);
}
} if(!rc)
{
puts(created_line);
}
}
} /* Note: yes we are going to leak 'buffer' * this is on purpose, to avoid cloning the 'key' out of it and our special * 'hash' just store the pointer to the key inside of buffer, hence it need * to remain allocated
*/ // coverity[leaked_storage] - this is on purpose return rc;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.