forked from apache/arrow
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ARROW-8795: [C++] Limited iOS support
Per suggestion from [JIRA](https://issues.apache.org/jira/browse/ARROW-8795), opening up a pull request to potentially get the ball rolling on having iOS as one of the supported platforms. Per writeup in [this doc](https://github.com/UnfoldedInc/deck.gl-native-dependencies/blob/master/docs/iOS-BUILD.md#arrow-v0170), there are a few issues with cmake's Xcode generator that require a few workarounds even when using their most recent versions. The writeup also describes issues that we've encountered building the library, as well as issues that were not resolved as they use optional features we are currently not making use of. Closes apache#7189 from ilijapuaca/feature/ios-support Authored-by: Ilija Puaca <[email protected]> Signed-off-by: Antoine Pitrou <[email protected]>
- Loading branch information
1 parent
c7baa7c
commit 43cb8d4
Showing
5 changed files
with
352 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,340 @@ | ||
// | ||
// The MIT License (MIT) | ||
// | ||
// Copyright (c) 2016 Alexander Kormanovsky | ||
// | ||
// Permission is hereby granted, free of charge, to any person obtaining a copy | ||
// of this software and associated documentation files (the "Software"), to deal | ||
// in the Software without restriction, including without limitation the rights | ||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
// copies of the Software, and to permit persons to whom the Software is | ||
// furnished to do so, subject to the following conditions: | ||
// | ||
// The above copyright notice and this permission notice shall be included in all | ||
// copies or substantial portions of the Software. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
// SOFTWARE. | ||
// | ||
|
||
#include "ios.h" | ||
|
||
#if TARGET_OS_IPHONE | ||
|
||
#include <Foundation/Foundation.h> | ||
|
||
#include <fstream> | ||
#include <zlib.h> | ||
#include <sys/stat.h> | ||
|
||
#ifndef TAR_DEBUG | ||
# define TAR_DEBUG 0 | ||
#endif | ||
|
||
#define INTERNAL_DIR "Library" | ||
#define TZDATA_DIR "tzdata" | ||
#define TARGZ_EXTENSION "tar.gz" | ||
|
||
#define TAR_BLOCK_SIZE 512 | ||
#define TAR_TYPE_POSITION 156 | ||
#define TAR_NAME_POSITION 0 | ||
#define TAR_NAME_SIZE 100 | ||
#define TAR_SIZE_POSITION 124 | ||
#define TAR_SIZE_SIZE 12 | ||
|
||
namespace arrow_vendored | ||
{ | ||
namespace date | ||
{ | ||
namespace iOSUtils | ||
{ | ||
|
||
struct TarInfo | ||
{ | ||
char objType; | ||
std::string objName; | ||
size_t realContentSize; // writable size without padding zeroes | ||
size_t blocksContentSize; // adjusted size to 512 bytes blocks | ||
bool success; | ||
}; | ||
|
||
std::string convertCFStringRefPathToCStringPath(CFStringRef ref); | ||
bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath); | ||
TarInfo getTarObjectInfo(std::ifstream &readStream); | ||
std::string getTarObject(std::ifstream &readStream, int64_t size); | ||
bool writeFile(const std::string &tzdataPath, const std::string &fileName, | ||
const std::string &data, size_t realContentSize); | ||
|
||
std::string | ||
get_current_timezone() | ||
{ | ||
CFTimeZoneRef tzRef = CFTimeZoneCopySystem(); | ||
CFStringRef tzNameRef = CFTimeZoneGetName(tzRef); | ||
CFIndex bufferSize = CFStringGetLength(tzNameRef) + 1; | ||
char buffer[bufferSize]; | ||
|
||
if (CFStringGetCString(tzNameRef, buffer, bufferSize, kCFStringEncodingUTF8)) | ||
{ | ||
CFRelease(tzRef); | ||
return std::string(buffer); | ||
} | ||
|
||
CFRelease(tzRef); | ||
|
||
return ""; | ||
} | ||
|
||
std::string | ||
get_tzdata_path() | ||
{ | ||
CFURLRef homeUrlRef = CFCopyHomeDirectoryURL(); | ||
CFStringRef homePath = CFURLCopyPath(homeUrlRef); | ||
std::string path(std::string(convertCFStringRefPathToCStringPath(homePath)) + | ||
INTERNAL_DIR + "/" + TZDATA_DIR); | ||
std::string result_path(std::string(convertCFStringRefPathToCStringPath(homePath)) + | ||
INTERNAL_DIR); | ||
|
||
if (access(path.c_str(), F_OK) == 0) | ||
{ | ||
#if TAR_DEBUG | ||
printf("tzdata dir exists\n"); | ||
#endif | ||
CFRelease(homeUrlRef); | ||
CFRelease(homePath); | ||
|
||
return result_path; | ||
} | ||
|
||
CFBundleRef mainBundle = CFBundleGetMainBundle(); | ||
CFArrayRef paths = CFBundleCopyResourceURLsOfType(mainBundle, CFSTR(TARGZ_EXTENSION), | ||
NULL); | ||
|
||
if (CFArrayGetCount(paths) != 0) | ||
{ | ||
// get archive path, assume there is no other tar.gz in bundle | ||
CFURLRef archiveUrl = static_cast<CFURLRef>(CFArrayGetValueAtIndex(paths, 0)); | ||
CFStringRef archiveName = CFURLCopyPath(archiveUrl); | ||
archiveUrl = CFBundleCopyResourceURL(mainBundle, archiveName, NULL, NULL); | ||
|
||
extractTzdata(homeUrlRef, archiveUrl, path); | ||
|
||
CFRelease(archiveUrl); | ||
CFRelease(archiveName); | ||
} | ||
|
||
CFRelease(homeUrlRef); | ||
CFRelease(homePath); | ||
CFRelease(paths); | ||
|
||
return result_path; | ||
} | ||
|
||
std::string | ||
convertCFStringRefPathToCStringPath(CFStringRef ref) | ||
{ | ||
CFIndex bufferSize = CFStringGetMaximumSizeOfFileSystemRepresentation(ref); | ||
char *buffer = new char[bufferSize]; | ||
CFStringGetFileSystemRepresentation(ref, buffer, bufferSize); | ||
auto result = std::string(buffer); | ||
delete[] buffer; | ||
return result; | ||
} | ||
|
||
bool | ||
extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath) | ||
{ | ||
std::string TAR_TMP_PATH = "/tmp.tar"; | ||
|
||
CFStringRef homeStringRef = CFURLCopyPath(homeUrl); | ||
auto homePath = convertCFStringRefPathToCStringPath(homeStringRef); | ||
CFRelease(homeStringRef); | ||
|
||
CFStringRef archiveStringRef = CFURLCopyPath(archiveUrl); | ||
auto archivePath = convertCFStringRefPathToCStringPath(archiveStringRef); | ||
CFRelease(archiveStringRef); | ||
|
||
// create Library path | ||
auto libraryPath = homePath + INTERNAL_DIR; | ||
|
||
// create tzdata path | ||
auto tzdataPath = libraryPath + "/" + TZDATA_DIR; | ||
|
||
// -- replace %20 with " " | ||
const std::string search = "%20"; | ||
const std::string replacement = " "; | ||
size_t pos = 0; | ||
|
||
while ((pos = archivePath.find(search, pos)) != std::string::npos) { | ||
archivePath.replace(pos, search.length(), replacement); | ||
pos += replacement.length(); | ||
} | ||
|
||
gzFile tarFile = gzopen(archivePath.c_str(), "rb"); | ||
|
||
// create tar unpacking path | ||
auto tarPath = libraryPath + TAR_TMP_PATH; | ||
|
||
// create tzdata directory | ||
mkdir(destPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); | ||
|
||
// ======= extract tar ======== | ||
|
||
std::ofstream os(tarPath.c_str(), std::ofstream::out | std::ofstream::app); | ||
unsigned int bufferLength = 1024 * 256; // 256Kb | ||
unsigned char *buffer = (unsigned char *)malloc(bufferLength); | ||
bool success = true; | ||
|
||
while (true) | ||
{ | ||
int readBytes = gzread(tarFile, buffer, bufferLength); | ||
|
||
if (readBytes > 0) | ||
{ | ||
os.write((char *) &buffer[0], readBytes); | ||
} | ||
else | ||
if (readBytes == 0) | ||
{ | ||
break; | ||
} | ||
else | ||
if (readBytes == -1) | ||
{ | ||
printf("decompression failed\n"); | ||
success = false; | ||
break; | ||
} | ||
else | ||
{ | ||
printf("unexpected zlib state\n"); | ||
success = false; | ||
break; | ||
} | ||
} | ||
|
||
os.close(); | ||
free(buffer); | ||
gzclose(tarFile); | ||
|
||
if (!success) | ||
{ | ||
remove(tarPath.c_str()); | ||
return false; | ||
} | ||
|
||
// ======== extract files ========= | ||
|
||
uint64_t location = 0; // Position in the file | ||
|
||
// get file size | ||
struct stat stat_buf; | ||
int res = stat(tarPath.c_str(), &stat_buf); | ||
if (res != 0) | ||
{ | ||
printf("error file size\n"); | ||
remove(tarPath.c_str()); | ||
return false; | ||
} | ||
int64_t tarSize = stat_buf.st_size; | ||
|
||
// create read stream | ||
std::ifstream is(tarPath.c_str(), std::ifstream::in | std::ifstream::binary); | ||
|
||
// process files | ||
while (location < tarSize) | ||
{ | ||
TarInfo info = getTarObjectInfo(is); | ||
|
||
if (!info.success || info.realContentSize == 0) | ||
{ | ||
break; // something wrong or all files are read | ||
} | ||
|
||
switch (info.objType) | ||
{ | ||
case '0': // file | ||
case '\0': // | ||
{ | ||
std::string obj = getTarObject(is, info.blocksContentSize); | ||
#if TAR_DEBUG | ||
size += info.realContentSize; | ||
printf("#%i %s file size %lld written total %ld from %lld\n", ++count, | ||
info.objName.c_str(), info.realContentSize, size, tarSize); | ||
#endif | ||
writeFile(tzdataPath, info.objName, obj, info.realContentSize); | ||
location += info.blocksContentSize; | ||
|
||
break; | ||
} | ||
} | ||
} | ||
|
||
remove(tarPath.c_str()); | ||
|
||
return true; | ||
} | ||
|
||
TarInfo | ||
getTarObjectInfo(std::ifstream &readStream) | ||
{ | ||
int64_t length = TAR_BLOCK_SIZE; | ||
char buffer[length]; | ||
char type; | ||
char name[TAR_NAME_SIZE + 1]; | ||
char sizeBuf[TAR_SIZE_SIZE + 1]; | ||
|
||
readStream.read(buffer, length); | ||
|
||
memcpy(&type, &buffer[TAR_TYPE_POSITION], 1); | ||
|
||
memset(&name, '\0', TAR_NAME_SIZE + 1); | ||
memcpy(&name, &buffer[TAR_NAME_POSITION], TAR_NAME_SIZE); | ||
|
||
memset(&sizeBuf, '\0', TAR_SIZE_SIZE + 1); | ||
memcpy(&sizeBuf, &buffer[TAR_SIZE_POSITION], TAR_SIZE_SIZE); | ||
size_t realSize = strtol(sizeBuf, NULL, 8); | ||
size_t blocksSize = realSize + (TAR_BLOCK_SIZE - (realSize % TAR_BLOCK_SIZE)); | ||
|
||
return {type, std::string(name), realSize, blocksSize, true}; | ||
} | ||
|
||
std::string | ||
getTarObject(std::ifstream &readStream, int64_t size) | ||
{ | ||
char buffer[size]; | ||
readStream.read(buffer, size); | ||
return std::string(buffer); | ||
} | ||
|
||
bool | ||
writeFile(const std::string &tzdataPath, const std::string &fileName, const std::string &data, | ||
size_t realContentSize) | ||
{ | ||
std::ofstream os(tzdataPath + "/" + fileName, std::ofstream::out | std::ofstream::binary); | ||
|
||
if (!os) { | ||
return false; | ||
} | ||
|
||
// trim empty space | ||
char trimmedData[realContentSize + 1]; | ||
memset(&trimmedData, '\0', realContentSize); | ||
memcpy(&trimmedData, data.c_str(), realContentSize); | ||
|
||
// write | ||
os.write(trimmedData, realContentSize); | ||
os.close(); | ||
|
||
return true; | ||
} | ||
|
||
} // namespace iOSUtils | ||
} // namespace date | ||
} // namespace arrow_vendored | ||
|
||
#endif // TARGET_OS_IPHONE |