From 3de43b55a7b6fcbaa2aa1d93cc5f8a2bab87f0f0 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 16 Feb 2022 16:57:47 -0800 Subject: [PATCH] Significantly reduce memory usage all around Added a string dictionary for deduplication of metadata, and actually initialize both it and the art dictionary on startup, so they actually work like they should. Signed-off-by: Christopher Snowhill --- Playlist/PlaylistEntry.m | 31 +++++++------ Utils/SQLiteStore.h | 1 + Utils/SQLiteStore.m | 99 ++++++++++++++++++++++++++++++---------- 3 files changed, 91 insertions(+), 40 deletions(-) diff --git a/Playlist/PlaylistEntry.m b/Playlist/PlaylistEntry.m index ed1097422..b7c49512b 100644 --- a/Playlist/PlaylistEntry.m +++ b/Playlist/PlaylistEntry.m @@ -460,6 +460,7 @@ [self setMetadataLoaded:YES]; } +// Now we duplicate the object to a new handle, but merely reference the same data - (id)copyWithZone:(NSZone *)zone { PlaylistEntry *pe = [[[self class] allocWithZone:zone] init]; @@ -479,22 +480,22 @@ pe->queuePosition = queuePosition; pe->error = error; - pe->errorMessage = [errorMessage copyWithZone:zone]; + pe->errorMessage = errorMessage; - pe->URL = [URL copyWithZone:zone]; + pe->URL = URL; - pe->artist = [artist copyWithZone:zone]; - pe->albumartist = [albumartist copyWithZone:zone]; - pe->album = [album copyWithZone:zone]; - pe->title = [title copyWithZone:zone]; - pe->genre = [genre copyWithZone:zone]; - pe->year = [year copyWithZone:zone]; - pe->track = [track copyWithZone:zone]; - pe->disc = [disc copyWithZone:zone]; + pe->artist = artist; + pe->albumartist = albumartist; + pe->album = album; + pe->title = title; + pe->genre = genre; + pe->year = year; + pe->track = track; + pe->disc = disc; - pe->cuesheet = [cuesheet copyWithZone:zone]; + pe->cuesheet = cuesheet; - pe->albumArtInternal = albumArtInternal; // Only allocated item not duplicated + pe->albumArtInternal = albumArtInternal; pe->replayGainAlbumGain = replayGainAlbumGain; pe->replayGainAlbumPeak = replayGainAlbumPeak; @@ -513,11 +514,11 @@ pe->Unsigned = Unsigned; pe->sampleRate = sampleRate; - pe->codec = [codec copyWithZone:zone]; + pe->codec = codec; - pe->endian = [endian copyWithZone:zone]; + pe->endian = endian; - pe->encoding = [encoding copyWithZone:zone]; + pe->encoding = encoding; pe->seekable = seekable; diff --git a/Utils/SQLiteStore.h b/Utils/SQLiteStore.h index 5b4cf0424..281d8427c 100644 --- a/Utils/SQLiteStore.h +++ b/Utils/SQLiteStore.h @@ -20,6 +20,7 @@ @private NSMutableArray *databaseMirror; NSMutableDictionary *artTable; + NSMutableDictionary *stringTable; } @property(nonatomic, readwrite) NSString *databasePath; diff --git a/Utils/SQLiteStore.m b/Utils/SQLiteStore.m index dfc60f313..419f29fd6 100644 --- a/Utils/SQLiteStore.m +++ b/Utils/SQLiteStore.m @@ -511,7 +511,7 @@ NSURL *urlForPath(NSString *path) { } @interface SQLiteStore (Private) -- (int64_t)addString:(NSString *)string; +- (int64_t)addString:(NSString **)string; - (NSString *)getString:(int64_t)stringId; - (void)removeString:(int64_t)stringId; - (int64_t)addArt:(NSData *)art; @@ -668,6 +668,8 @@ static SQLiteStore *g_sharedStore = NULL; size_t count = [self playlistGetCount]; databaseMirror = [[NSMutableArray alloc] init]; + artTable = [[NSMutableDictionary alloc] init]; + stringTable = [[NSMutableDictionary alloc] init]; for(size_t i = 0; i < count; ++i) { PlaylistEntry *pe = [self playlistGetItem:i]; @@ -690,12 +692,12 @@ static SQLiteStore *g_sharedStore = NULL; } } -- (int64_t)addString:(NSString *)string { - if(!string || [string length] == 0) { +- (int64_t)addString:(NSString **)string { + if(!*string || [*string length] == 0) { return -1; } - const char *str = [string UTF8String]; + const char *str = [*string UTF8String]; uint64_t len = strlen(str); // SQLite expects number of bytes, not characters sqlite3_stmt *st = stmt[stmt_select_string]; @@ -732,6 +734,8 @@ static SQLiteStore *g_sharedStore = NULL; ret = sqlite3_last_insert_rowid(g_database); refcount = 1; + + [stringTable setObject:*string forKey:[[NSNumber numberWithInteger:ret] stringValue]]; } else { st = stmt[stmt_bump_string]; @@ -741,6 +745,8 @@ static SQLiteStore *g_sharedStore = NULL; sqlite3_reset(st)) { return -1; } + + *string = [stringTable objectForKey:[[NSNumber numberWithInteger:ret] stringValue]]; } return ret; @@ -750,6 +756,9 @@ static SQLiteStore *g_sharedStore = NULL; if(stringId < 0) return @""; + NSString *ret = [stringTable objectForKey:[[NSNumber numberWithInteger:stringId] stringValue]]; + if(ret) return ret; + sqlite3_stmt *st = stmt[stmt_select_string_value]; if(sqlite3_reset(st) || @@ -764,7 +773,7 @@ static SQLiteStore *g_sharedStore = NULL; return @""; } - NSString *ret = @""; + ret = @""; if(rc == SQLITE_ROW) { const unsigned char *str = sqlite3_column_text(st, select_string_value_out_value); @@ -817,6 +826,8 @@ static SQLiteStore *g_sharedStore = NULL; sqlite3_reset(st)) { return; } + + [stringTable removeObjectForKey:[[NSNumber numberWithInteger:stringId] stringValue]]; } else { st = stmt[stmt_pop_string]; @@ -978,7 +989,7 @@ static SQLiteStore *g_sharedStore = NULL; NSURL *url = [track URL]; NSString *urlString = [url absoluteString]; - int64_t urlId = [self addString:urlString]; + int64_t urlId = [self addString:&urlString]; sqlite3_stmt *st = stmt[stmt_select_track]; @@ -1005,14 +1016,31 @@ static SQLiteStore *g_sharedStore = NULL; sqlite3_reset(stmt[stmt_select_string]); if(rc != SQLITE_ROW) { - int64_t albumId = [self addString:[track album]]; - int64_t albumartistId = [self addString:[track albumartist]]; - int64_t artistId = [self addString:[track artist]]; - int64_t titleId = [self addString:[track rawTitle]]; - int64_t genreId = [self addString:[track genre]]; - int64_t codecId = [self addString:[track codec]]; - int64_t cuesheetId = [self addString:[track cuesheet]]; - int64_t encodingId = [self addString:[track encoding]]; + NSString *temp; + temp = [track album]; + int64_t albumId = [self addString:&temp]; + [track setAlbum:temp]; + temp = [track albumartist]; + int64_t albumartistId = [self addString:&temp]; + [track setAlbumartist:temp]; + temp = [track artist]; + int64_t artistId = [self addString:&temp]; + [track setArtist:temp]; + temp = [track rawTitle]; + int64_t titleId = [self addString:&temp]; + [track setTitle:temp]; + temp = [track genre]; + int64_t genreId = [self addString:&temp]; + [track setGenre:temp]; + temp = [track codec]; + int64_t codecId = [self addString:&temp]; + [track setCodec:temp]; + temp = [track cuesheet]; + int64_t cuesheetId = [self addString:&temp]; + [track setCuesheet:temp]; + temp = [track encoding]; + int64_t encodingId = [self addString:&temp]; + [track setEncoding:temp]; int64_t trackNr = [[track track] intValue] | (((uint64_t)[[track disc] intValue]) << 32); int64_t year = [[track year] intValue]; int64_t unsignedFmt = [track Unsigned]; @@ -1021,7 +1049,9 @@ static SQLiteStore *g_sharedStore = NULL; int64_t bitspersample = [track bitsPerSample]; int64_t channels = [track channels]; int64_t channelConfig = [track channelConfig]; - int64_t endianId = [self addString:[track endian]]; + temp = [track endian]; + int64_t endianId = [self addString:&temp]; + [track setEndian:temp]; int64_t floatingpoint = [track floatingPoint]; int64_t totalframes = [track totalFrames]; int64_t metadataloaded = [track metadataLoaded]; @@ -1113,7 +1143,7 @@ static SQLiteStore *g_sharedStore = NULL; NSURL *url = [track URL]; NSString *urlString = [url absoluteString]; - int64_t urlId = [self addString:urlString]; + int64_t urlId = [self addString:&urlString]; sqlite3_stmt *st = stmt[stmt_select_track]; @@ -1201,14 +1231,31 @@ static SQLiteStore *g_sharedStore = NULL; sqlite3_reset(st); { - int64_t albumId = [self addString:[track album]]; - int64_t albumartistId = [self addString:[track albumartist]]; - int64_t artistId = [self addString:[track artist]]; - int64_t titleId = [self addString:[track rawTitle]]; - int64_t genreId = [self addString:[track genre]]; - int64_t codecId = [self addString:[track codec]]; - int64_t cuesheetId = [self addString:[track cuesheet]]; - int64_t encodingId = [self addString:[track encoding]]; + NSString *temp; + temp = [track album]; + int64_t albumId = [self addString:&temp]; + [track setAlbum:temp]; + temp = [track albumartist]; + int64_t albumartistId = [self addString:&temp]; + [track setAlbumartist:temp]; + temp = [track artist]; + int64_t artistId = [self addString:&temp]; + [track setArtist:temp]; + temp = [track rawTitle]; + int64_t titleId = [self addString:&temp]; + [track setTitle:temp]; + temp = [track genre]; + int64_t genreId = [self addString:&temp]; + [track setGenre:temp]; + temp = [track codec]; + int64_t codecId = [self addString:&temp]; + [track setCodec:temp]; + temp = [track cuesheet]; + int64_t cuesheetId = [self addString:&temp]; + [track setCuesheet:temp]; + temp = [track encoding]; + int64_t encodingId = [self addString:&temp]; + [track setEncoding:temp]; int64_t trackNr = [[track track] intValue] | (((uint64_t)[[track disc] intValue]) << 32); int64_t year = [[track year] intValue]; int64_t unsignedFmt = [track Unsigned]; @@ -1217,7 +1264,9 @@ static SQLiteStore *g_sharedStore = NULL; int64_t bitspersample = [track bitsPerSample]; int64_t channels = [track channels]; int64_t channelConfig = [track channelConfig]; - int64_t endianId = [self addString:[track endian]]; + temp = [track endian]; + int64_t endianId = [self addString:&temp]; + [track setEndian:temp]; int64_t floatingpoint = [track floatingPoint]; int64_t totalframes = [track totalFrames]; int64_t metadataloaded = [track metadataLoaded];