Index: .fossil-settings/clean-glob ================================================================== --- .fossil-settings/clean-glob +++ .fossil-settings/clean-glob @@ -37,11 +37,13 @@ tests/EBOOT.PBP tests/Info.plist tests/PARAM.SFO tests/objc_sync/objc_sync tests/plugin/Info.plist +tests/serialization_xml.m tests/terminal/terminal_tests +tests/testfile_bin.m tests/tests tests/tests.3dsx tests/tests.arm9 tests/tests.nds tests/tests.nro Index: .fossil-settings/ignore-glob ================================================================== --- .fossil-settings/ignore-glob +++ .fossil-settings/ignore-glob @@ -42,11 +42,13 @@ tests/iOS.xcodeproj/*.pbxuser tests/iOS.xcodeproj/project.xcworkspace tests/iOS.xcodeproj/xcuserdata tests/objc_sync/objc_sync tests/plugin/Info.plist +tests/serialization_xml.m tests/terminal/terminal_tests +tests/testfile_bin.m tests/tests tests/tests.3dsx tests/tests.arm9 tests/tests.nds tests/tests.nro Index: .gitignore ================================================================== --- .gitignore +++ .gitignore @@ -42,11 +42,13 @@ tests/iOS.xcodeproj/*.pbxuser tests/iOS.xcodeproj/project.xcworkspace tests/iOS.xcodeproj/xcuserdata tests/objc_sync/objc_sync tests/plugin/Info.plist +tests/serialization_xml.m tests/terminal/terminal_tests +tests/testfile_bin.m tests/tests tests/tests.3dsx tests/tests.arm9 tests/tests.nds tests/tests.nro Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -20,10 +20,11 @@ OFData.m \ OFData+CryptographicHashing.m \ OFData+MessagePackParsing.m \ OFDate.m \ OFDictionary.m \ + OFEmbeddedFileURLHandler.m \ OFEnumerator.m \ OFFileManager.m \ OFGZIPStream.m \ OFHMAC.m \ OFInflate64Stream.m \ ADDED src/OFEmbeddedFileURLHandler.h Index: src/OFEmbeddedFileURLHandler.h ================================================================== --- src/OFEmbeddedFileURLHandler.h +++ src/OFEmbeddedFileURLHandler.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2008-2022 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFURLHandler.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFEmbeddedFileURLHandler: OFURLHandler +@end + +OF_ASSUME_NONNULL_END ADDED src/OFEmbeddedFileURLHandler.m Index: src/OFEmbeddedFileURLHandler.m ================================================================== --- src/OFEmbeddedFileURLHandler.m +++ src/OFEmbeddedFileURLHandler.m @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2008-2022 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "OFEmbeddedFileURLHandler.h" +#import "OFMemoryStream.h" +#import "OFURL.h" + +#import "OFInvalidArgumentException.h" +#import "OFOpenItemFailedException.h" + +#ifdef OF_HAVE_THREADS +# import "OFOnce.h" +# import "OFPlainMutex.h" +#endif + +struct EmbeddedFile { + const char *name; + const uint8_t *bytes; + size_t size; +} *embeddedFiles = NULL; +size_t numEmbeddedFiles = 0; +#ifdef OF_HAVE_THREADS +static OFPlainMutex mutex; + +static void +init(void) +{ + OFEnsure(OFPlainMutexNew(&mutex) == 0); +} +#endif + +void +OFRegisterEmbeddedFile(const char *name, const uint8_t *bytes, size_t size) +{ +#ifdef OF_HAVE_THREADS + static OFOnceControl onceControl = OFOnceControlInitValue; + OFOnce(&onceControl, init); + + OFEnsure(OFPlainMutexLock(&mutex) == 0); +#endif + + embeddedFiles = realloc(embeddedFiles, + sizeof(*embeddedFiles) * (numEmbeddedFiles + 1)); + OFEnsure(embeddedFiles != NULL); + + embeddedFiles[numEmbeddedFiles].name = name; + embeddedFiles[numEmbeddedFiles].bytes = bytes; + embeddedFiles[numEmbeddedFiles].size = size; + numEmbeddedFiles++; + +#ifdef OF_HAVE_THREADS + OFEnsure(OFPlainMutexUnlock(&mutex) == 0); +#endif +} + +@implementation OFEmbeddedFileURLHandler +- (OFStream *)openItemAtURL: (OFURL *)URL mode: (OFString *)mode +{ + const char *path; + + if (![mode isEqual: @"r"]) + @throw [OFOpenItemFailedException exceptionWithURL: URL + mode: mode + errNo: EROFS]; + + if (URL.host != nil || URL.port != nil || URL.user != nil || + URL.password != nil || URL.query != nil || URL.fragment != nil) + @throw [OFInvalidArgumentException exception]; + + if ((path = URL.path.UTF8String) == NULL) + @throw [OFInvalidArgumentException exception]; + +#ifdef OF_HAVE_THREADS + OFEnsure(OFPlainMutexLock(&mutex) == 0); + @try { + for (size_t i = 0; i < numEmbeddedFiles; i++) { + if (strcmp(embeddedFiles[i].name, path) != 0) + continue; + + return [OFMemoryStream + streamWithMemoryAddress: (void *) + embeddedFiles[i].bytes + size: embeddedFiles[i].size + writable: false]; + } +#endif +#ifdef OF_HAVE_THREADS + } @finally { + OFEnsure(OFPlainMutexUnlock(&mutex) == 0); + } +#endif + + @throw [OFOpenItemFailedException exceptionWithURL: URL + mode: mode + errNo: ENOENT]; +} +@end Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -1082,10 +1082,14 @@ data = [OFData dataWithContentsOfURL: URL]; } @catch (id e) { [self release]; @throw e; } + + /* FIXME: Detect encoding where we can. */ + if (encoding == OFStringEncodingAutodetect) + encoding = OFStringEncodingUTF8; self = [self initWithCString: data.items encoding: encoding length: data.count * data.itemSize]; Index: src/OFURL.m ================================================================== --- src/OFURL.m +++ src/OFURL.m @@ -555,15 +555,21 @@ if (portString.unsignedLongLongValue > 65535) @throw [OFInvalidFormatException exception]; _port = [[OFNumber alloc] initWithUnsignedShort: portString.unsignedLongLongValue]; - } else + } else { _URLEncodedHost = [[OFString alloc] initWithUTF8String: UTF8String]; - if (!isIPv6Host) + if (_URLEncodedHost.length == 0) { + [_URLEncodedHost release]; + _URLEncodedHost = nil; + } + } + + if (_URLEncodedHost != nil && !isIPv6Host) OFURLVerifyIsEscaped(_URLEncodedHost, [OFCharacterSet URLHostAllowedCharacterSet]); if ((UTF8String = tmp) != NULL) { if ((tmp = strchr(UTF8String, '#')) != NULL) { Index: src/OFURLHandler.m ================================================================== --- src/OFURLHandler.m +++ src/OFURLHandler.m @@ -22,10 +22,11 @@ #ifdef OF_HAVE_THREADS # import "OFMutex.h" #endif +#import "OFEmbeddedFileURLHandler.h" #ifdef OF_HAVE_FILES # import "OFFileURLHandler.h" #endif #if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) # import "OFHTTPURLHandler.h" @@ -54,10 +55,12 @@ #ifdef OF_HAVE_THREADS mutex = [[OFMutex alloc] init]; atexit(releaseMutex); #endif + [self registerClass: [OFEmbeddedFileURLHandler class] + forScheme: @"objfw-embedded"]; #ifdef OF_HAVE_FILES [self registerClass: [OFFileURLHandler class] forScheme: @"file"]; #endif #if defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) [self registerClass: [OFHTTPURLHandler class] forScheme: @"http"]; Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -6,11 +6,13 @@ CLEAN = EBOOT.PBP \ boot.dol \ ${PROG_NOINST}.arm9 \ ${PROG_NOINST}.nds \ - ${PROG_NOINST}.nro + ${PROG_NOINST}.nro \ + serialization_xml.m \ + testfile_bin.m DISTCLEAN = Info.plist PROG_NOINST = tests${PROG_SUFFIX} STATIC_LIB_NOINST = ${TESTS_STATIC_LIB} SRCS = ForwardingTests.m \ @@ -46,11 +48,13 @@ TestsAppDelegate.m \ ${USE_SRCS_FILES} \ ${USE_SRCS_PLUGINS} \ ${USE_SRCS_SOCKETS} \ ${USE_SRCS_THREADS} \ - ${USE_SRCS_WINDOWS} + ${USE_SRCS_WINDOWS} \ + serialization_xml.m \ + testfile_bin.m SRCS_FILES = OFHMACTests.m \ OFINIFileTests.m \ OFMD5HashTests.m \ OFRIPEMD160HashTests.m \ OFSerializationTests.m \ @@ -81,10 +85,15 @@ IOS_USER ?= mobile IOS_TMP ?= /tmp/objfw-test include ../buildsys.mk +serialization_xml.m: serialization.xml + ${SHELL} ../utils/objfw-embed $< /$< $@ +testfile_bin.m: testfile.bin + ${SHELL} ../utils/objfw-embed $< /$< $@ + .PHONY: run run-on-ios run-on-android run: rm -f libobjfw.so.${OBJFW_LIB_MAJOR} rm -f libobjfw.so.${OBJFW_LIB_MAJOR_MINOR} rm -f objfw${OBJFW_LIB_MAJOR}.dll libobjfw.${OBJFW_LIB_MAJOR}.dylib Index: tests/OFHMACTests.m ================================================================== --- tests/OFHMACTests.m +++ tests/OFHMACTests.m @@ -49,11 +49,13 @@ @implementation TestsAppDelegate (OFHMACTests) - (void)HMACTests { void *pool = objc_autoreleasePoolPush(); - OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"]; + OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; + OFStream *file = [[OFURLHandler handlerForURL: URL] + openItemAtURL: URL mode: @"r"]; OFHMAC *HMACMD5, *HMACSHA1, *HMACRMD160; OFHMAC *HMACSHA256, *HMACSHA384, *HMACSHA512; TEST(@"+[HMACWithHashClass:] with MD5", (HMACMD5 = [OFHMAC HMACWithHashClass: [OFMD5Hash class] Index: tests/OFMD5HashTests.m ================================================================== --- tests/OFMD5HashTests.m +++ tests/OFMD5HashTests.m @@ -27,11 +27,13 @@ @implementation TestsAppDelegate (OFMD5HashTests) - (void)MD5HashTests { void *pool = objc_autoreleasePoolPush(); OFMD5Hash *MD5, *MD5Copy; - OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"]; + OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; + OFStream *file = [[OFURLHandler handlerForURL: URL] + openItemAtURL: URL mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (MD5 = [OFMD5Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFRIPEMD160HashTests.m ================================================================== --- tests/OFRIPEMD160HashTests.m +++ tests/OFRIPEMD160HashTests.m @@ -28,11 +28,13 @@ @implementation TestsAppDelegate (OFRIPEMD160HashTests) - (void)RIPEMD160HashTests { void *pool = objc_autoreleasePoolPush(); OFRIPEMD160Hash *RIPEMD160, *RIPEMD160Copy; - OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"]; + OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; + OFStream *file = [[OFURLHandler handlerForURL: URL] + openItemAtURL: URL mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (RIPEMD160 = [OFRIPEMD160Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA1HashTests.m ================================================================== --- tests/OFSHA1HashTests.m +++ tests/OFSHA1HashTests.m @@ -28,11 +28,13 @@ @implementation TestsAppDelegate (SHA1HashTests) - (void)SHA1HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA1Hash *SHA1, *SHA1Copy; - OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"]; + OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; + OFStream *file = [[OFURLHandler handlerForURL: URL] + openItemAtURL: URL mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA1 = [OFSHA1Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA224HashTests.m ================================================================== --- tests/OFSHA224HashTests.m +++ tests/OFSHA224HashTests.m @@ -28,11 +28,13 @@ @implementation TestsAppDelegate (SHA224HashTests) - (void)SHA224HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA224Hash *SHA224, *SHA224Copy; - OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"]; + OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; + OFStream *file = [[OFURLHandler handlerForURL: URL] + openItemAtURL: URL mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA224 = [OFSHA224Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA256HashTests.m ================================================================== --- tests/OFSHA256HashTests.m +++ tests/OFSHA256HashTests.m @@ -28,11 +28,13 @@ @implementation TestsAppDelegate (SHA256HashTests) - (void)SHA256HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA256Hash *SHA256, *SHA256Copy; - OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"]; + OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; + OFStream *file = [[OFURLHandler handlerForURL: URL] + openItemAtURL: URL mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA256 = [OFSHA256Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA384HashTests.m ================================================================== --- tests/OFSHA384HashTests.m +++ tests/OFSHA384HashTests.m @@ -29,11 +29,13 @@ @implementation TestsAppDelegate (SHA384HashTests) - (void)SHA384HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA384Hash *SHA384, *SHA384Copy; - OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"]; + OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; + OFStream *file = [[OFURLHandler handlerForURL: URL] + openItemAtURL: URL mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA384 = [OFSHA384Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSHA512HashTests.m ================================================================== --- tests/OFSHA512HashTests.m +++ tests/OFSHA512HashTests.m @@ -30,11 +30,13 @@ @implementation TestsAppDelegate (SHA512HashTests) - (void)SHA512HashTests { void *pool = objc_autoreleasePoolPush(); OFSHA512Hash *SHA512, *SHA512Copy; - OFFile *file = [OFFile fileWithPath: @"testfile.bin" mode: @"r"]; + OFURL *URL = [OFURL URLWithString: @"objfw-embedded:///testfile.bin"]; + OFStream *file = [[OFURLHandler handlerForURL: URL] + openItemAtURL: URL mode: @"r"]; TEST(@"+[hashWithAllowsSwappableMemory:]", (SHA512 = [OFSHA512Hash hashWithAllowsSwappableMemory: true])) while (!file.atEndOfStream) { Index: tests/OFSerializationTests.m ================================================================== --- tests/OFSerializationTests.m +++ tests/OFSerializationTests.m @@ -59,13 +59,14 @@ UUIDWithUUIDString: @"01234567-89AB-CDEF-FEDC-BA9876543210"]; [dict setObject: @"uuid" forKey: UUID]; TEST(@"-[stringBySerializing]", (string = dict.stringBySerializing) && [string isEqual: - [OFString stringWithContentsOfFile: @"serialization.xml"]]) + [OFString stringWithContentsOfURL: [OFURL URLWithString: + @"objfw-embedded:///serialization.xml"]]]) TEST(@"-[objectByDeserializing]", [string.objectByDeserializing isEqual: dict]) objc_autoreleasePoolPop(pool); } @end Index: utils/Makefile ================================================================== --- utils/Makefile +++ utils/Makefile @@ -9,11 +9,11 @@ include ../buildsys.mk DISTCLEAN = objfw-config install-extra: objfw-config objfw-compile objfw-new - for i in objfw-config objfw-compile objfw-new; do \ + for i in objfw-config objfw-compile objfw-embed objfw-new; do \ ${INSTALL_STATUS}; \ if ${MKDIR_P} ${DESTDIR}${bindir} && ${INSTALL} -m 755 $$i ${DESTDIR}${bindir}/${BIN_PREFIX}$$i; then \ ${INSTALL_OK}; \ else \ ${INSTALL_FAILED}; \ ADDED utils/objfw-embed Index: utils/objfw-embed ================================================================== --- utils/objfw-embed +++ utils/objfw-embed @@ -0,0 +1,26 @@ +#!/bin/sh +if test $# != 3; then + echo "Usage: $0 source_file filename output.m" 1>&2 + exit 1 +fi + +exec 1>$3 + +cat < +#include + +extern void OFRegisterEmbeddedFile(const char *, const uint8_t *, size_t); + +static const uint8_t bytes[] = { +EOF +od -tx1 $1 | cut -s -d' ' -f 2- | sed 's/^/ 0x/;s/ /, 0x/g;s/$/,/' +cat <