From 8f399e94c6b9022aef08c0860d372f678f53729a Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Sat, 6 Jan 2024 00:03:54 +0100 Subject: [PATCH] hdcpc chunker caching and memory allocation reduction (#228) - **FIXED** Use `Longtail_SetReAllocAndFree` in command main - **FIXED** Reduced memory allocation count and memory usage - Added caching of hpcdc chunkers to reduce memory churn - Reduced memory allocation count when indexing source - Use temp-buffers when creating platform specific paths (Win32) - **FIXED** Fixed memory leaks in longtail command --- cmd/main.c | 14 +- lib/hpcdcchunker/longtail_hpcdcchunker.c | 92 +++++++++--- lib/longtail_platform.c | 170 +++++++++++++++++------ src/longtail.c | 168 ++++++++++++++-------- 4 files changed, 319 insertions(+), 125 deletions(-) diff --git a/cmd/main.c b/cmd/main.c index d9fc827d..f333439a 100644 --- a/cmd/main.c +++ b/cmd/main.c @@ -2140,7 +2140,7 @@ int main(int argc, char** argv) if (enable_mem_tracer_raw) { Longtail_MemTracer_Init(); - Longtail_SetAllocAndFree(Longtail_MemTracer_Alloc, Longtail_MemTracer_Free); + Longtail_SetReAllocAndFree(Longtail_MemTracer_ReAlloc, Longtail_MemTracer_Free); } uint32_t compression = ParseCompressionType(compression_raw); @@ -2218,7 +2218,7 @@ int main(int argc, char** argv) if (enable_mem_tracer_raw) { Longtail_MemTracer_Init(); - Longtail_SetAllocAndFree(Longtail_MemTracer_Alloc, Longtail_MemTracer_Free); + Longtail_SetReAllocAndFree(Longtail_MemTracer_ReAlloc, Longtail_MemTracer_Free); } const char* cache_path = cache_path_raw ? NormalizePath(cache_path_raw) : 0; @@ -2266,7 +2266,7 @@ int main(int argc, char** argv) if (enable_mem_tracer_raw) { Longtail_MemTracer_Init(); - Longtail_SetAllocAndFree(Longtail_MemTracer_Alloc, Longtail_MemTracer_Free); + Longtail_SetReAllocAndFree(Longtail_MemTracer_ReAlloc, Longtail_MemTracer_Free); } const char* version_index_path = NormalizePath(version_index_path_raw); @@ -2294,7 +2294,7 @@ int main(int argc, char** argv) if (enable_mem_tracer_raw) { Longtail_MemTracer_Init(); - Longtail_SetAllocAndFree(Longtail_MemTracer_Alloc, Longtail_MemTracer_Free); + Longtail_SetReAllocAndFree(Longtail_MemTracer_ReAlloc, Longtail_MemTracer_Free); } if (kgflags_get_non_flag_args_count() < 2) @@ -2348,7 +2348,7 @@ int main(int argc, char** argv) if (enable_mem_tracer_raw) { Longtail_MemTracer_Init(); - Longtail_SetAllocAndFree(Longtail_MemTracer_Alloc, Longtail_MemTracer_Free); + Longtail_SetReAllocAndFree(Longtail_MemTracer_ReAlloc, Longtail_MemTracer_Free); } const char* source_path_raw = kgflags_get_non_flag_arg(1); @@ -2409,7 +2409,7 @@ int main(int argc, char** argv) if (enable_mem_tracer_raw) { Longtail_MemTracer_Init(); - Longtail_SetAllocAndFree(Longtail_MemTracer_Alloc, Longtail_MemTracer_Free); + Longtail_SetReAllocAndFree(Longtail_MemTracer_ReAlloc, Longtail_MemTracer_Free); } uint32_t compression = ParseCompressionType(compression_raw); @@ -2474,7 +2474,7 @@ int main(int argc, char** argv) if (enable_mem_tracer_raw) { Longtail_MemTracer_Init(); - Longtail_SetAllocAndFree(Longtail_MemTracer_Alloc, Longtail_MemTracer_Free); + Longtail_SetReAllocAndFree(Longtail_MemTracer_ReAlloc, Longtail_MemTracer_Free); } const char* source_path = NormalizePath(source_path_raw); diff --git a/lib/hpcdcchunker/longtail_hpcdcchunker.c b/lib/hpcdcchunker/longtail_hpcdcchunker.c index 5313a232..0f695f46 100644 --- a/lib/hpcdcchunker/longtail_hpcdcchunker.c +++ b/lib/hpcdcchunker/longtail_hpcdcchunker.c @@ -2,6 +2,8 @@ #include "longtail_hpcdcchunker.h" +#include "../longtail_platform.h" + #include #include #include @@ -97,6 +99,16 @@ struct HPCDCArray uint32_t len; }; +#define HPCDCCHUNKER_MAX_CACHED_CHUNKER_COUNT 232 + +struct Longtail_HPCDCChunkerAPI +{ + struct Longtail_ChunkerAPI m_API; + HLongtail_SpinLock m_Lock; + struct Longtail_HPCDCChunker* m_CachedChunkers[HPCDCCHUNKER_MAX_CACHED_CHUNKER_COUNT]; + uint32_t m_CachedChunkerCount; +}; + struct Longtail_HPCDCChunker { struct Longtail_HPCDCChunkerParams params; @@ -116,12 +128,16 @@ static uint32_t HPCDCDiscriminatorFromAvg(double avg) return (uint32_t)(avg / (-1.42888852e-7*avg + 1.33237515)); } +// TODO: Create a cache for chunkers? + int Longtail_HPCDCCreateChunker( struct Longtail_HPCDCChunkerParams* params, + struct Longtail_HPCDCChunker* optional_cached_chunker, struct Longtail_HPCDCChunker** out_chunker) { MAKE_LOG_CONTEXT_FIELDS(ctx) LONGTAIL_LOGFIELD(params, "%p"), + LONGTAIL_LOGFIELD(optional_cached_chunker, "%p"), LONGTAIL_LOGFIELD(out_chunker, "%p") MAKE_LOG_CONTEXT_WITH_FIELDS(ctx, 0, LONGTAIL_LOG_LEVEL_DEBUG) @@ -137,17 +153,26 @@ int Longtail_HPCDCCreateChunker( max_feed = 0xffffffffu; } - size_t chunker_size = sizeof(struct Longtail_HPCDCChunker) + max_feed; - struct Longtail_HPCDCChunker* c = (struct Longtail_HPCDCChunker*)Longtail_Alloc("HPCDCCreateChunker", chunker_size); - if (!c) + struct Longtail_HPCDCChunker* c = optional_cached_chunker; + if (c == 0 || c->max_feed < max_feed) { - LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "Longtail_Alloc() failed with %d", ENOMEM) - return ENOMEM; + if (optional_cached_chunker) + { + Longtail_Free(optional_cached_chunker); + optional_cached_chunker = 0; + } + size_t chunker_size = sizeof(struct Longtail_HPCDCChunker) + max_feed; + c = (struct Longtail_HPCDCChunker*)Longtail_Alloc("HPCDCCreateChunker", chunker_size); + if (!c) + { + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "Longtail_Alloc() failed with %d", ENOMEM) + return ENOMEM; + } + c->buf.data = (uint8_t*)&c[1]; + c->max_feed = (uint32_t)max_feed; } c->params = *params; - c->buf.data = (uint8_t*)&c[1]; c->buf.len = 0; - c->max_feed = (uint32_t)max_feed; c->off = 0; c->hValue = 0; c->hDiscriminator = HPCDCDiscriminatorFromAvg((double)params->avg); @@ -286,12 +311,6 @@ struct Longtail_Chunker_ChunkRange Longtail_HPCDCNextChunk( return r; } -struct Longtail_HPCDCChunkerAPI -{ - struct Longtail_ChunkerAPI m_API; - uint32_t m_TargetChunkSize; -}; - void HPCDCChunker_Dispose(struct Longtail_API* base_api) { MAKE_LOG_CONTEXT_FIELDS(ctx) @@ -300,6 +319,15 @@ void HPCDCChunker_Dispose(struct Longtail_API* base_api) LONGTAIL_FATAL_ASSERT(ctx, base_api, return) struct Longtail_HPCDCChunkerAPI* api = (struct Longtail_HPCDCChunkerAPI*)base_api; + Longtail_LockSpinLock(api->m_Lock); + while (api->m_CachedChunkerCount > 0) + { + api->m_CachedChunkerCount--; + Longtail_Free(api->m_CachedChunkers[api->m_CachedChunkerCount]); + api->m_CachedChunkers[api->m_CachedChunkerCount] = 0; + } + Longtail_UnlockSpinLock(api->m_Lock); + Longtail_DeleteSpinLock(api->m_Lock); Longtail_Free(api); } @@ -338,14 +366,23 @@ int HPCDCChunker_CreateChunker( LONGTAIL_VALIDATE_INPUT(ctx, out_chunker, return EINVAL) struct Longtail_HPCDCChunkerAPI* api = (struct Longtail_HPCDCChunkerAPI*)chunker_api; - - struct Longtail_HPCDCChunkerParams chunker_params; + struct Longtail_HPCDCChunkerParams chunker_params; chunker_params.min = min_chunk_size; chunker_params.avg = avg_chunk_size; chunker_params.max = max_chunk_size; + struct Longtail_HPCDCChunker* cached_chunker = 0; + Longtail_LockSpinLock(api->m_Lock); + if (api->m_CachedChunkerCount > 0) + { + api->m_CachedChunkerCount--; + cached_chunker = api->m_CachedChunkers[api->m_CachedChunkerCount]; + api->m_CachedChunkers[api->m_CachedChunkerCount] = 0; + } + Longtail_UnlockSpinLock(api->m_Lock); + struct Longtail_HPCDCChunker* chunker; - Longtail_HPCDCCreateChunker(&chunker_params, &chunker); + Longtail_HPCDCCreateChunker(&chunker_params, cached_chunker, &chunker); *out_chunker = (Longtail_ChunkerAPI_HChunker)chunker; @@ -399,7 +436,18 @@ int HPCDCChunker_DisposeChunker(struct Longtail_ChunkerAPI* chunker_api, Longtai LONGTAIL_VALIDATE_INPUT(ctx, chunker_api, return EINVAL) LONGTAIL_VALIDATE_INPUT(ctx, chunker, return EINVAL) struct Longtail_HPCDCChunkerAPI* api = (struct Longtail_HPCDCChunkerAPI*)chunker_api; - Longtail_Free(chunker); + Longtail_LockSpinLock(api->m_Lock); + if (api->m_CachedChunkerCount == HPCDCCHUNKER_MAX_CACHED_CHUNKER_COUNT) + { + Longtail_UnlockSpinLock(api->m_Lock); + Longtail_Free(chunker); + } + else + { + api->m_CachedChunkers[api->m_CachedChunkerCount] = (struct Longtail_HPCDCChunker*)chunker; + api->m_CachedChunkerCount++; + Longtail_UnlockSpinLock(api->m_Lock); + } return 0; } @@ -502,6 +550,13 @@ static int HPCDCChunker_Init( } struct Longtail_HPCDCChunkerAPI* api = (struct Longtail_HPCDCChunkerAPI*)chunker_api; + int err = Longtail_CreateSpinLock(&api[1], &api->m_Lock); + if (err) + { + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "Longtail_CreateSpinLock() failed with %d", err) + return err; + } + api->m_CachedChunkerCount = 0; *out_chunker_api = chunker_api; return 0; @@ -512,7 +567,8 @@ struct Longtail_ChunkerAPI* Longtail_CreateHPCDCChunkerAPI() MAKE_LOG_CONTEXT(ctx, 0, LONGTAIL_LOG_LEVEL_DEBUG) size_t api_size = - sizeof(struct Longtail_HPCDCChunkerAPI); + sizeof(struct Longtail_HPCDCChunkerAPI)+ + Longtail_GetSpinLockSize(); void* mem = Longtail_Alloc("HPCDCCreateChunker", api_size); if (!mem) diff --git a/lib/longtail_platform.c b/lib/longtail_platform.c index 6504e63c..dedfd984 100644 --- a/lib/longtail_platform.c +++ b/lib/longtail_platform.c @@ -416,25 +416,45 @@ void Longtail_UnlockRWLockWrite(HLongtail_RWLock rwlock) ReleaseSRWLockExclusive(&rwlock->m_RWLock); } -static wchar_t* MakeWCharString(const char* s) +static wchar_t* MakeWCharString(const char* s, wchar_t* buffer, size_t buffer_size) { + struct Longtail_LogContextFmt_Private* ctx = 0; int l = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0); - wchar_t* r = (wchar_t*)Longtail_Alloc("MakeWCharString", l * sizeof(wchar_t)); + if (l <= 0) + { + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_WARNING, "MultiByteToWideChar for path `%s`: failed with %d", s, Win32ErrorToErrno(GetLastError())) + return 0; + } + wchar_t* r = buffer; + if ((size_t)l > buffer_size) + { + r = (wchar_t*)Longtail_Alloc("MakeWCharString", l * sizeof(wchar_t)); + } MultiByteToWideChar(CP_UTF8, 0, s, -1, r, l); return r; } -static char* MakeUTF8String(const wchar_t* s) +static char* MakeUTF8String(const wchar_t* s, char* buffer, size_t buffer_size) { + struct Longtail_LogContextFmt_Private* ctx = 0; int l = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, NULL ,NULL); - char* r = (char*)Longtail_Alloc("MakeUTF8String", l * sizeof(char)); + if (l <= 0) + { + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_WARNING, "MakeUTF8String for path `%ls`: failed with %d", s, Win32ErrorToErrno(GetLastError())) + return 0; + } + char* r = buffer; + if ((size_t)l > buffer_size) + { + r = (char*)Longtail_Alloc("MakeUTF8String", l * sizeof(char)); + } WideCharToMultiByte(CP_UTF8, 0, s, -1, r, l, NULL ,NULL); return r; } -static wchar_t* MakePlatformPath(const char* path) +static wchar_t* MakePlatformPath(const char* path, wchar_t* buffer, size_t buffer_size) { - wchar_t* r = MakeWCharString(path); + wchar_t* r = MakeWCharString(path, buffer, buffer_size); wchar_t* p = r; while (*p) { @@ -444,13 +464,16 @@ static wchar_t* MakePlatformPath(const char* path) return r; } -static wchar_t* MakeLongPlatformPath(const char* path) +static wchar_t* MakeLongPlatformPath(const char* path, wchar_t* buffer, size_t buffer_size) { - wchar_t* platform_path = MakePlatformPath(path); + wchar_t* platform_path = MakePlatformPath(path, buffer, buffer_size); const wchar_t* r = MakeLongPath(platform_path); if (r != platform_path) { - Longtail_Free(platform_path); + if (platform_path != buffer) + { + Longtail_Free(platform_path); + } } return (wchar_t*)r; } @@ -574,16 +597,20 @@ static DWORD NativeCreateDirectory(wchar_t* long_path) int Longtail_CreateDirectory(const char* path) { - wchar_t* long_path = MakeLongPlatformPath(path); + wchar_t long_path_buffer[512]; + wchar_t* long_path = MakeLongPlatformPath(path, long_path_buffer, sizeof(long_path_buffer)); DWORD error = NativeCreateDirectory(long_path); - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } return Win32ErrorToErrno(error); } int Longtail_MoveFile(const char* source, const char* target) { - wchar_t* long_source_path = MakeLongPlatformPath(source); - wchar_t* long_target_path = MakeLongPlatformPath(target); + wchar_t* long_source_path = MakeLongPlatformPath(source, 0, 0); + wchar_t* long_target_path = MakeLongPlatformPath(target, 0, 0); BOOL ok = MoveFileW(long_source_path, long_target_path); Longtail_Free(long_source_path); Longtail_Free(long_target_path); @@ -604,9 +631,13 @@ int Longtail_IsDir(const char* path) struct Longtail_LogContextFmt_Private* ctx = 0; #endif // defined(LONGTAIL_ASSERTS) - wchar_t* long_path = MakeLongPlatformPath(path); + wchar_t long_path_buffer[512]; + wchar_t* long_path = MakeLongPlatformPath(path, long_path_buffer, sizeof(long_path_buffer)); DWORD attrs = GetFileAttributesW(long_path); - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } if (attrs == INVALID_FILE_ATTRIBUTES) { int e = Win32ErrorToErrno(GetLastError()); @@ -630,9 +661,13 @@ int Longtail_IsFile(const char* path) struct Longtail_LogContextFmt_Private* ctx = 0; #endif // defined(LONGTAIL_ASSERTS) - wchar_t* long_path = MakeLongPlatformPath(path); + wchar_t long_path_buffer[512]; + wchar_t* long_path = MakeLongPlatformPath(path, long_path_buffer, sizeof(long_path_buffer)); DWORD attrs = GetFileAttributesW(long_path); - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } if (attrs == INVALID_FILE_ATTRIBUTES) { int e = Win32ErrorToErrno(GetLastError()); @@ -648,9 +683,13 @@ int Longtail_IsFile(const char* path) int Longtail_RemoveDir(const char* path) { - wchar_t* long_path = MakeLongPlatformPath(path); + wchar_t long_path_buffer[512]; + wchar_t* long_path = MakeLongPlatformPath(path, long_path_buffer, sizeof(long_path_buffer)); BOOL ok = RemoveDirectoryW(long_path); - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } if (ok) { return 0; @@ -660,9 +699,13 @@ int Longtail_RemoveDir(const char* path) int Longtail_RemoveFile(const char* path) { - wchar_t* long_path = MakeLongPlatformPath(path); + wchar_t long_path_buffer[512]; + wchar_t* long_path = MakeLongPlatformPath(path, long_path_buffer, sizeof(long_path_buffer)); BOOL ok = DeleteFileW(long_path); - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } if (ok) { return 0; @@ -670,12 +713,15 @@ int Longtail_RemoveFile(const char* path) return Win32ErrorToErrno(GetLastError()); } +#define LONGTAIL_FSITERATOR_PRIVATE_ITEM_BUFFER_SIZE (1024 - sizeof(WIN32_FIND_DATAW) - sizeof(HANDLE) - sizeof(wchar_t*) - sizeof(char*)) + struct Longtail_FSIterator_private { WIN32_FIND_DATAW m_FindData; HANDLE m_Handle; wchar_t* m_Path; char* m_ItemPath; + char m_ItemPathBuffer[LONGTAIL_FSITERATOR_PRIVATE_ITEM_BUFFER_SIZE]; }; size_t Longtail_GetFSIteratorSize() @@ -719,7 +765,7 @@ static int Skip(HLongtail_FSIterator fs_iterator) int Longtail_StartFind(HLongtail_FSIterator fs_iterator, const char* path) { - fs_iterator->m_Path = MakeLongPlatformPath(path); + fs_iterator->m_Path = MakeLongPlatformPath(path, 0, 0); wchar_t* scan_pattern = ConcatPlatformPath(fs_iterator->m_Path, L"*.*"); fs_iterator->m_ItemPath = 0; fs_iterator->m_Handle = FindFirstFileW(scan_pattern, &fs_iterator->m_FindData); @@ -749,7 +795,10 @@ int Longtail_FindNext(HLongtail_FSIterator fs_iterator) void Longtail_CloseFind(HLongtail_FSIterator fs_iterator) { - Longtail_Free(fs_iterator->m_ItemPath); + if (fs_iterator->m_ItemPath != fs_iterator->m_ItemPathBuffer) + { + Longtail_Free(fs_iterator->m_ItemPath); + } Longtail_Free(fs_iterator->m_Path); FindClose(fs_iterator->m_Handle); fs_iterator->m_Handle = INVALID_HANDLE_VALUE; @@ -761,8 +810,11 @@ const char* Longtail_GetFileName(HLongtail_FSIterator fs_iterator) { return 0; } - Longtail_Free(fs_iterator->m_ItemPath); - fs_iterator->m_ItemPath = MakeUTF8String(fs_iterator->m_FindData.cFileName); + if (fs_iterator->m_ItemPath != fs_iterator->m_ItemPathBuffer) + { + Longtail_Free(fs_iterator->m_ItemPath); + } + fs_iterator->m_ItemPath = MakeUTF8String(fs_iterator->m_FindData.cFileName, fs_iterator->m_ItemPathBuffer, sizeof(fs_iterator->m_ItemPathBuffer)); return fs_iterator->m_ItemPath; } @@ -770,8 +822,11 @@ const char* Longtail_GetDirectoryName(HLongtail_FSIterator fs_iterator) { if (fs_iterator->m_FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - Longtail_Free(fs_iterator->m_ItemPath); - fs_iterator->m_ItemPath = MakeUTF8String(fs_iterator->m_FindData.cFileName); + if (fs_iterator->m_ItemPath != fs_iterator->m_ItemPathBuffer) + { + Longtail_Free(fs_iterator->m_ItemPath); + } + fs_iterator->m_ItemPath = MakeUTF8String(fs_iterator->m_FindData.cFileName, fs_iterator->m_ItemPathBuffer, sizeof(fs_iterator->m_ItemPathBuffer)); return fs_iterator->m_ItemPath; } return 0; @@ -829,10 +884,14 @@ DWORD NativeOpenReadFileWithRetry(wchar_t* long_path, HANDLE* out_handle) int Longtail_OpenReadFile(const char* path, HLongtail_OpenFile* out_read_file) { - wchar_t* long_path = MakeLongPlatformPath(path); + wchar_t long_path_buffer[512]; + wchar_t* long_path = MakeLongPlatformPath(path, long_path_buffer, sizeof(long_path_buffer)); HANDLE handle; DWORD error = NativeOpenReadFileWithRetry(long_path, &handle); - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } if (error != ERROR_SUCCESS) { return Win32ErrorToErrno(error); @@ -870,10 +929,14 @@ DWORD NativeOpenWriteFileWithRetry(wchar_t* long_path, DWORD create_disposition, int Longtail_OpenWriteFile(const char* path, uint64_t initial_size, HLongtail_OpenFile* out_write_file) { - wchar_t* long_path = MakeLongPlatformPath(path); + wchar_t long_path_buffer[512]; + wchar_t* long_path = MakeLongPlatformPath(path, long_path_buffer, sizeof(long_path_buffer)); HANDLE handle; DWORD error = NativeOpenWriteFileWithRetry(long_path, initial_size == 0 ? CREATE_ALWAYS : OPEN_ALWAYS, &handle); - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } if (error != ERROR_SUCCESS) { return Win32ErrorToErrno(error); @@ -928,12 +991,16 @@ int Longtail_SetFilePermissions(const char* path, uint16_t permissions) struct Longtail_LogContextFmt_Private* ctx = 0; #endif // defined(LONGTAIL_ASSERTS) - wchar_t* long_path = MakeLongPlatformPath(path); + wchar_t long_path_buffer[512]; + wchar_t* long_path = MakeLongPlatformPath(path, long_path_buffer, sizeof(long_path_buffer)); DWORD attrs = GetFileAttributesW(long_path); if (attrs == INVALID_FILE_ATTRIBUTES) { - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } int e = Win32ErrorToErrno(GetLastError()); if (e == ENOENT) { @@ -956,7 +1023,10 @@ int Longtail_SetFilePermissions(const char* path, uint16_t permissions) } if (FALSE == SetFileAttributesW(long_path, attrs)) { - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } int e = Win32ErrorToErrno(GetLastError()); if (e == ENOENT) { @@ -966,7 +1036,10 @@ int Longtail_SetFilePermissions(const char* path, uint16_t permissions) return e; } } - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } return 0; } @@ -981,9 +1054,13 @@ int Longtail_GetFilePermissions(const char* path, uint16_t* out_permissions) struct Longtail_LogContextFmt_Private* ctx = 0; #endif // defined(LONGTAIL_ASSERTS) - wchar_t* long_path = MakeLongPlatformPath(path); + wchar_t long_path_buffer[512]; + wchar_t* long_path = MakeLongPlatformPath(path, long_path_buffer, sizeof(long_path_buffer)); DWORD attrs = GetFileAttributesW(long_path); - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } if (attrs == INVALID_FILE_ATTRIBUTES) { int e = Win32ErrorToErrno(GetLastError()); @@ -1163,7 +1240,8 @@ size_t Longtail_GetFileLockSize() int Longtail_LockFile(void* mem, const char* path, HLongtail_FileLock* out_file_lock) { - wchar_t* long_path = MakeLongPlatformPath(path); + wchar_t long_path_buffer[512]; + wchar_t* long_path = MakeLongPlatformPath(path, long_path_buffer, sizeof(long_path_buffer)); *out_file_lock = (HLongtail_FileLock)mem; (*out_file_lock)->handle = INVALID_HANDLE_VALUE; @@ -1183,7 +1261,10 @@ int Longtail_LockFile(void* mem, const char* path, HLongtail_FileLock* out_file_ { if (--try_count == 0) { - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } return EACCES; } Longtail_Sleep(retry_delay); @@ -1213,12 +1294,19 @@ int Longtail_LockFile(void* mem, const char* path, HLongtail_FileLock* out_file_ case ERROR_OPLOCK_NOT_GRANTED: break; default: - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } + (long_path); return Win32ErrorToErrno(error); } retry_delay += 2000; } - Longtail_Free(long_path); + if (long_path != long_path_buffer) + { + Longtail_Free(long_path); + } (*out_file_lock)->handle = handle; return 0; } diff --git a/src/longtail.c b/src/longtail.c index 7c0c9fb6..0654cd48 100644 --- a/src/longtail.c +++ b/src/longtail.c @@ -1307,6 +1307,7 @@ static int RecurseTree( break; } LONGTAIL_LOG(ctx2, LONGTAIL_LOG_LEVEL_DEBUG, "Scanning `%s`", full_search_path) + char asset_path_buffer[1024]; while(err == 0) { struct Longtail_StorageAPI_EntryProperties properties; @@ -1326,11 +1327,18 @@ static int RecurseTree( { size_t current_relative_path_length = strlen(relative_parent_path); size_t new_parent_path_length = current_relative_path_length + 1 + strlen(properties.m_Name); - asset_path = (char*)Longtail_Alloc("GetFilesRecursively", new_parent_path_length + 1); - if (!asset_path) + if (new_parent_path_length < sizeof(asset_path_buffer)) { - LONGTAIL_LOG(ctx2, LONGTAIL_LOG_LEVEL_WARNING, "Longtail_Alloc() failed with %d", ENOMEM) - break; + asset_path = asset_path_buffer; + } + else + { + asset_path = (char*)Longtail_Alloc("GetFilesRecursively", new_parent_path_length + 1); + if (!asset_path) + { + LONGTAIL_LOG(ctx2, LONGTAIL_LOG_LEVEL_WARNING, "Longtail_Alloc() failed with %d", ENOMEM) + break; + } } strcpy(asset_path, relative_parent_path); asset_path[current_relative_path_length] = '/'; @@ -1359,7 +1367,10 @@ static int RecurseTree( { LONGTAIL_LOG(ctx3, LONGTAIL_LOG_LEVEL_WARNING, "entry_processor() failed with %d", err) - Longtail_Free(asset_path); + if (asset_path != asset_path_buffer) + { + Longtail_Free(asset_path); + } asset_path = 0; break; } @@ -1378,11 +1389,21 @@ static int RecurseTree( } } arrput(full_search_paths, storage_api->ConcatPath(storage_api, full_search_path, properties.m_Name)); - arrput(relative_parent_paths, asset_path); - asset_path = 0; + if (asset_path != asset_path_buffer) + { + arrput(relative_parent_paths, asset_path); + asset_path = 0; + } + else + { + arrput(relative_parent_paths, Longtail_Strdup(asset_path)); + } } } - Longtail_Free(asset_path); + if (asset_path != asset_path_buffer) + { + Longtail_Free(asset_path); + } } err = storage_api->FindNext(storage_api, fs_iterator); if (err == ENOENT) @@ -1425,7 +1446,7 @@ static struct Longtail_FileInfos* CreateFileInfos(uint32_t path_count, uint32_t LONGTAIL_FATAL_ASSERT(ctx, (path_count == 0 && path_data_size == 0) || (path_count > 0 && path_data_size > path_count), return 0) size_t file_infos_size = GetFileInfosSize(path_count, path_data_size); - struct Longtail_FileInfos* file_infos = (struct Longtail_FileInfos*)Longtail_Alloc("GetFilesRecursively", file_infos_size); + struct Longtail_FileInfos* file_infos = (struct Longtail_FileInfos*)Longtail_Alloc("CreateFileInfos", file_infos_size); if (!file_infos) { LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "Longtail_Alloc() failed with %d", ENOMEM) @@ -1494,6 +1515,7 @@ int LongtailPrivate_MakeFileInfos( static int AppendPath( struct Longtail_FileInfos** file_infos, const char* path, + int is_dir, uint64_t file_size, uint16_t file_permissions, uint32_t* max_path_count, @@ -1520,7 +1542,8 @@ static int AppendPath( LONGTAIL_FATAL_ASSERT(ctx, max_data_size != 0, return EINVAL) LONGTAIL_FATAL_ASSERT(ctx, path_count_increment > 0, return EINVAL) LONGTAIL_FATAL_ASSERT(ctx, data_size_increment > 0, return EINVAL) - uint32_t path_size = (uint32_t)(strlen(path) + 1); + size_t path_length = strlen(path); + uint32_t path_size = (uint32_t)(path_length + 1) + (is_dir ? 1 : 0); int out_of_path_data = (*file_infos)->m_PathDataSize + path_size > *max_data_size; int out_of_path_count = (*file_infos)->m_Count >= *max_path_count; @@ -1551,7 +1574,17 @@ static int AppendPath( *file_infos = new_file_infos; } - memmove(&(*file_infos)->m_PathData[(*file_infos)->m_PathDataSize], path, path_size); + char* path_write_ptr = &(*file_infos)->m_PathData[(*file_infos)->m_PathDataSize]; + memmove(path_write_ptr, path, path_length); + if (is_dir) + { + path_write_ptr[path_length] = '/'; + path_write_ptr[path_length + 1] = '\0'; + } + else + { + path_write_ptr[path_length] = '\0'; + } (*file_infos)->m_PathStartOffsets[(*file_infos)->m_Count] = (*file_infos)->m_PathDataSize; (*file_infos)->m_PathDataSize += path_size; (*file_infos)->m_Sizes[(*file_infos)->m_Count] = file_size; @@ -1588,31 +1621,12 @@ static int AddFile(void* context, const char* root_path, const char* asset_path, struct AddFile_Context* paths_context = (struct AddFile_Context*)context; struct Longtail_StorageAPI* storage_api = paths_context->m_StorageAPI; - char* full_path = (char*)asset_path; - if (properties->m_IsDir) - { - size_t asset_path_length = strlen(asset_path); - full_path = (char*)Longtail_Alloc("GetFilesRecursively", asset_path_length + 1 + 1); - strcpy(full_path, asset_path); - full_path[asset_path_length] = '/'; - full_path[asset_path_length + 1] = 0; - } - - int err = AppendPath(&paths_context->m_FileInfos, full_path, properties->m_Size, properties->m_Permissions, &paths_context->m_ReservedPathCount, &paths_context->m_ReservedPathSize, 1024, 1024 * 32); + int err = AppendPath(&paths_context->m_FileInfos, asset_path, properties->m_IsDir, properties->m_Size, properties->m_Permissions, &paths_context->m_ReservedPathCount, &paths_context->m_ReservedPathSize, 1024, 1024 * 32); if (err) { LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "AppendPath() failed with %d", err) - if (full_path != asset_path) - { - Longtail_Free(full_path); - } return err; } - - if (full_path != asset_path) - { - Longtail_Free(full_path); - } return 0; } @@ -1712,6 +1726,8 @@ static int StorageChunkFeederFunc(void* context, Longtail_ChunkerAPI_HChunker ch return 0; } +#define HASHJOB_INLINE_CHUNK_HASH_COUNT 16 + struct HashJob { struct Longtail_StorageAPI* m_StorageAPI; @@ -1729,6 +1745,9 @@ struct HashJob uint32_t m_TargetChunkSize; int m_EnableFileMap; int m_Err; + + TLongtail_Hash m_InlineChunkHashes[HASHJOB_INLINE_CHUNK_HASH_COUNT]; + uint32_t m_InlineChunkSizes[HASHJOB_INLINE_CHUNK_HASH_COUNT]; }; #define MIN_CHUNKER_SIZE(min_chunk_size, target_chunk_size) (((target_chunk_size / 8) < min_chunk_size) ? min_chunk_size : (target_chunk_size / 8)) @@ -1799,29 +1818,39 @@ static int DynamicChunking(void* context, uint32_t job_id, int is_cancelled) hash_job->m_Err = err; return 0; } + hash_job->m_ChunkHashes = hash_job->m_InlineChunkHashes; + hash_job->m_ChunkSizes = hash_job->m_InlineChunkSizes; if (hash_size <= chunker_min_size) { - void* output_mem = Longtail_Alloc("DynamicChunking", sizeof(TLongtail_Hash) + sizeof(uint32_t)); - hash_job->m_ChunkHashes = (TLongtail_Hash*)output_mem; - hash_job->m_ChunkSizes = (uint32_t*)&hash_job->m_ChunkHashes[1]; - - char* buffer = (char*)Longtail_Alloc("DynamicChunking", (size_t)hash_size); - if (!buffer) + char* buffer = 0; + void* heap_buffer = 0; + char stack_buffer[2048]; + if (chunker_min_size > sizeof(stack_buffer)) { - LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "Longtail_Alloc() failed with %d", ENOMEM) - storage_api->CloseFile(storage_api, file_handle); - file_handle = 0; - Longtail_Free(path); - path = 0; - hash_job->m_Err = ENOMEM; - return 0; + heap_buffer = Longtail_Alloc("DynamicChunking", (size_t)hash_size); + if (!buffer) + { + LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "Longtail_Alloc() failed with %d", ENOMEM) + storage_api->CloseFile(storage_api, file_handle); + file_handle = 0; + Longtail_Free(path); + path = 0; + hash_job->m_Err = ENOMEM; + return 0; + } + buffer = (char*)heap_buffer; + } + else + { + buffer = stack_buffer; } + err = storage_api->Read(storage_api, file_handle, hash_job->m_StartRange, hash_size, buffer); if (err) { LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "storage_api->Read() failed with %d", err) - Longtail_Free(buffer); - buffer = 0; + Longtail_Free(heap_buffer); + heap_buffer = 0; storage_api->CloseFile(storage_api, file_handle); file_handle = 0; Longtail_Free(path); @@ -1834,8 +1863,8 @@ static int DynamicChunking(void* context, uint32_t job_id, int is_cancelled) if (err) { LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "hash_job->m_HashAPI->HashBuffer() failed with %d", err) - Longtail_Free(buffer); - buffer = 0; + Longtail_Free(heap_buffer); + heap_buffer = 0; storage_api->CloseFile(storage_api, file_handle); file_handle = 0; Longtail_Free(path); @@ -1844,8 +1873,8 @@ static int DynamicChunking(void* context, uint32_t job_id, int is_cancelled) return 0; } - Longtail_Free(buffer); - buffer = 0; + Longtail_Free(heap_buffer); + heap_buffer = 0; hash_job->m_ChunkSizes[0] = (uint32_t)hash_size; @@ -1857,7 +1886,7 @@ static int DynamicChunking(void* context, uint32_t job_id, int is_cancelled) uint32_t avg_chunk_size = AVG_CHUNKER_SIZE(chunker_min_size, hash_job->m_TargetChunkSize); uint32_t max_chunk_size = MAX_CHUNKER_SIZE(chunker_min_size, hash_job->m_TargetChunkSize); - uint32_t chunk_capacity = 0; + uint32_t chunk_capacity = HASHJOB_INLINE_CHUNK_HASH_COUNT; Longtail_ChunkerAPI_HChunker chunker; err = hash_job->m_ChunkerAPI->CreateChunker(hash_job->m_ChunkerAPI, min_chunk_size, avg_chunk_size, max_chunk_size, &chunker); @@ -1888,7 +1917,9 @@ static int DynamicChunking(void* context, uint32_t job_id, int is_cancelled) uint64_t bytes_left = buffer_end_ptr - chunk_start_ptr; if (chunk_capacity == chunk_count) { - uint32_t new_chunk_capacity = chunk_count + 1 + (uint32_t)(bytes_left / avg_chunk_size); + uint64_t bytes_read = chunk_start_ptr - mapped_ptr; + uint64_t avg_size = bytes_read / chunk_count; + uint32_t new_chunk_capacity = chunk_count + 1 + (uint32_t)(bytes_left / avg_size); void* new_output_mem = Longtail_Alloc("DynamicChunking", sizeof(TLongtail_Hash) * new_chunk_capacity + sizeof(uint32_t) * new_chunk_capacity); if (!new_output_mem) @@ -1912,7 +1943,10 @@ static int DynamicChunking(void* context, uint32_t job_id, int is_cancelled) { memcpy(new_chunk_hashes, hash_job->m_ChunkHashes, sizeof(TLongtail_Hash) * chunk_count); memcpy(new_chunk_sizes, hash_job->m_ChunkSizes, sizeof(uint32_t) * chunk_count); - Longtail_Free(hash_job->m_ChunkHashes); + if (hash_job->m_ChunkHashes != hash_job->m_InlineChunkHashes) + { + Longtail_Free(hash_job->m_ChunkHashes); + } } hash_job->m_ChunkHashes = new_chunk_hashes; hash_job->m_ChunkSizes = new_chunk_sizes; @@ -1977,7 +2011,8 @@ static int DynamicChunking(void* context, uint32_t job_id, int is_cancelled) if (chunk_capacity == chunk_count) { uint64_t bytes_left = hash_size - (chunk_range.offset); - uint32_t new_chunk_capacity = chunk_count + 1 + (uint32_t)(bytes_left / avg_chunk_size); + uint64_t avg_size = chunk_range.offset / chunk_count; + uint32_t new_chunk_capacity = chunk_count + 1 + (uint32_t)(bytes_left / avg_size); void* new_output_mem = Longtail_Alloc("DynamicChunking", sizeof(TLongtail_Hash) * new_chunk_capacity + sizeof(uint32_t) * new_chunk_capacity); if (!new_output_mem) @@ -2000,7 +2035,10 @@ static int DynamicChunking(void* context, uint32_t job_id, int is_cancelled) { memcpy(new_chunk_hashes, hash_job->m_ChunkHashes, sizeof(TLongtail_Hash) * chunk_count); memcpy(new_chunk_sizes, hash_job->m_ChunkSizes, sizeof(uint32_t) * chunk_count); - Longtail_Free(hash_job->m_ChunkHashes); + if (hash_job->m_ChunkHashes != hash_job->m_InlineChunkHashes) + { + Longtail_Free(hash_job->m_ChunkHashes); + } } hash_job->m_ChunkHashes = new_chunk_hashes; hash_job->m_ChunkSizes = new_chunk_sizes; @@ -2248,7 +2286,10 @@ static int ChunkAssets( { for (uint32_t i = 0; i < jobs_submitted; ++i) { - Longtail_Free(tmp_hash_jobs[i].m_ChunkHashes); + if (tmp_hash_jobs[i].m_ChunkHashes != tmp_hash_jobs[i].m_InlineChunkHashes) + { + Longtail_Free(tmp_hash_jobs[i].m_ChunkHashes); + } } Longtail_Free(work_mem); return err; @@ -2278,7 +2319,10 @@ static int ChunkAssets( LONGTAIL_LOG(ctx, LONGTAIL_LOG_LEVEL_ERROR, "AllocChunkAssetsData() failed with %d", ENOMEM) for (uint32_t i = 0; i < jobs_submitted; ++i) { - Longtail_Free(tmp_hash_jobs[i].m_ChunkHashes); + if (tmp_hash_jobs[i].m_ChunkHashes != tmp_hash_jobs[i].m_InlineChunkHashes) + { + Longtail_Free(tmp_hash_jobs[i].m_ChunkHashes); + } } Longtail_Free(work_mem); return ENOMEM; @@ -2314,7 +2358,10 @@ static int ChunkAssets( Longtail_Free(cad); for (uint32_t i = 0; i < jobs_submitted; ++i) { - Longtail_Free(tmp_hash_jobs[i].m_ChunkHashes); + if (tmp_hash_jobs[i].m_ChunkHashes != tmp_hash_jobs[i].m_InlineChunkHashes) + { + Longtail_Free(tmp_hash_jobs[i].m_ChunkHashes); + } } Longtail_Free(work_mem); return err; @@ -2325,7 +2372,10 @@ static int ChunkAssets( for (uint32_t i = 0; i < jobs_submitted; ++i) { - Longtail_Free(tmp_hash_jobs[i].m_ChunkHashes); + if (tmp_hash_jobs[i].m_ChunkHashes != tmp_hash_jobs[i].m_InlineChunkHashes) + { + Longtail_Free(tmp_hash_jobs[i].m_ChunkHashes); + } } Longtail_Free(work_mem); return err;