How to convert items of NSArray to utf16 (char16_t)? - nsarray

I have a function that gets all contents of directory whether files or directories and I am using contentsOfDirectoryAtPath to collect the content of a directory then I save the names of files/directories into a container called contentsStore which accepts key&value items of UTF-16 string char16_t. look at the following code to make your vision clear:
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:_dirPath error:nil];
for(unsigned int i= 0; i< [dirContents count]; i++){
if(isDir){
// `contentsStore` is key&value container that accepts utf-16 string (char16_t)
contentsStore.Add([[dirContents objectAtIndex:i] UTF8String], "directory");
} else {
contentsStore.Add([[dirContents objectAtIndex:i] UTF8String], "file");
}
}
Note that I don't post the entire code because it's big but I just added the important parts that related to the problem. Also, I am using Objective-C just as a bridge to achieve my goal to use Cocoa in macOS but the main language that I use is C++, so, the entire code is a combination of C++/Objective-C.
How to make the objectAtIndex method to output the item's content as UTF-16 char16_t?

the following will give you an idea. The [Filemanager defaultmanager] is actually supporting your task. You can convert the path to C-string and convert then into string of char16_t aka basic_string<char16_t>.
NSString *_dirPath = [[NSBundle mainBundle] bundlePath];
NSError *error = nil;
NSFileManager *manager = [[NSFileManager defaultManager] init];
NSArray *dirContents = [manager contentsOfDirectoryAtPath:_dirPath error:&error];
if (!error && dirContents.count ) {
for(unsigned int i = 0; i < [dirContents count]; i++){
NSString *path = [dirContents objectAtIndex:i];
BOOL isDir;
std::string usingkey = "file";
if ([manager fileExistsAtPath:path isDirectory:&isDir] && isDir) {
usingkey = "directory";
}
const char *fileRepresentation = [manager fileSystemRepresentationWithPath:path];
// function declared below..
std::u16string char16string = to_utf16(fileRepresentation);
// and use it to store your C++ storageObject, value&key pair
// don't know of what datatype is usingkey in your source
// just assumed std::string
contentsStore.Add(char16string, usingkey);
}
}
you will have to include the following in your .mm implementation
#include <string>
#include <codecvt>
#include <iostream>
#implementation Yourclassname
//std::u16string is same as basic_string<char16_t>
std::u16string to_utf16( std::string str )
{ return std::wstring_convert< std::codecvt_utf8_utf16<char16_t>, char16_t >{}.from_bytes(str); }
#end

Related

Getting null when converting const char* to NSString Objective-C

I'm trying to convert a const char* that I'm getting from a C++ method that I have, to a NSString in a method in Objective-C. This is the method from c++:
#define EXPORT __attribute__((visibility("default")))
EXPORT
string getAudioWmark(const char* _filename)
{
string filename = string(_filename);
string result;
result = get_watermark(filename, "");
return (result.c_str());
}
This is what result is returning in the debugger view:
result std::string "pattern 0:00 f59e26b2f4668d02bd33e03a1c0d8892 0.774 0.774 CLIP-A\n\n"
This is the declaration of the method in the .h file from Objective-C:
extern const char* getAudioWmark(const char* filename);
#interface WatermarkLib : NSObject
- (void)getAudio:(NSString*)filename;
#end
And this is the .mm implementation of the method:
- (void)getAudio:(NSString*)filename {
const char *cString = [filename cStringUsingEncoding:NSASCIIStringEncoding];
NSString* externalString = [NSString stringWithCString:getAudioWmark(cString) encoding:NSUTF8StringEncoding];
NSLog(#"Exito! %#", externalString);
}
"externalString" is null, and that's what I'm trying to fix.
This should help you out.
const char *cString = filename.UTF8String;
But make sure filename has content otherwise it must result in null

CFDataRef vs NSData file hashing

I want to hash a file (using SHA1 at the moment). Here is the function:
static inline __attribute__((always_inline)) NSString *SHA1String(NSData *data) {
uint8_t digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(data.bytes, (CC_LONG)data.length, digest);
NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
[output appendFormat:#"%02x", digest[i]];
}
return output;
}
And here are the two different methods of loading the data:
CFURLRef filePath = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR("/path/to/file"), kCFURLPOSIXPathStyle, false);
CFDataRef cfFileData = CFURLCreateData(kCFAllocatorDefault, filePath, kCFStringEncodingUTF8, false);
NSString *cfFileHash = SHA1String((__bridge NSData *)cfFileData);
NSData *fileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:#"/path/to/file"]];
NSString *fileHash = SHA1String((__bridge NSData *)fileData);
NSLog(#"Hashes: %# - %#", cfFileHash, fileHash);
The hashes differ and I wonder what is causing this. I'd like to use the CoreFoundation API, but if the file hash differs, that'd be bad.
I use another file manager to view the sha1 of the file and it matches the one from NSData.
Any insights appreciated.

Objective-C & C++ interface GSCBufferString to string

I am writing an Objective-C wrapper for a C++ class. On the OC side I have to take an NSData (read from a json file) get a list out from it and convert that to std::vector< std::string >. Here is my code so far (in an mm file):
NSData *input = [NSData dataWithContentsOfFile:[NSString stringWithUTF8String:"1455469592904_acceldata742283.json"]];
NSError* error;
NSDictionary* jsonArray = [NSJSONSerialization JSONObjectWithData:input options:NSJSONReadingMutableContainers error:&error];
NSMutableArray *accdata = [jsonArray valueForKeyPath:#"data.accelerometer" ];
int size = [accdata count];
vector<string> cppAccdata;
for (int i = 0; i< size; i++){
//~ cppAccdata.push_back();
NSLog(#"%#",[accdata objectAtIndex:i]);
}
The accdata speaking loosely is a list of strings. The json file basically looks like this (with more lines but that's beside the point):
{
"data" : {
"accelerometer" : [
"1455463005.714 -8.311620700836182 -3.969735990142822 -3.737648066711425",
"1455463005.724 -8.256703700256347 -4.017769660949707 -3.680336864471435" ] }}
The output of the code is:
2016-02-15 14:18:16.212 test-OC[10300] 1455463066.241 -7.960421244812011 -4.400093738555908 -3.746177410125732
2016-02-15 14:18:16.212 test-OC[10300] 1455463066.261 -8.019677735900878 -4.377498460388184 -3.634847032928467
2016-02-15 14:18:16.212 test-OC[10300] GSCBufferString
Technically I need to convert [accdata objectAtIndex:i] to an std::string, which I guess would work through a C const char *, but I have not been able to find anything on converting a GSCBufferString into anything. How can I achieve this? There also might be a more elegant way to loop through accdata.
NSArray *accdata = [jsonArray valueForKeyPath:#"data.accelerometer" ];
vector<string> cppAccdata;
for (NSString *str in accdata) {
cppAccdata.push_back(str.UTF8String);
}
Try this:
NSArray *jsonArray;
NSArray *accData = [jsonArray valueForKeyPath:#"data.accelerometer"];
vector<string> cppAccdata;
for (NSString *str in accData){
std::string *accString = new std::string([str UTF8String]);
cppAccdata.push_back(*accString);
}
The two answer proposed were correct in the way of doing the conversion, but the loop syntax didn't compile. This workaround works though:
int size = [accdata count];
vector<string> cppAccdata;
for (int i = 0; i< size; i++){
cppAccdata.push_back([[accdata objectAtIndex:i] UTF8String]);
}

Memory leak in obj-c/c++ code

I'm using instruments and I have a lot of memory being leaked by this method. It is used everywhere in my app, and when I built it, I used some c++ (i think).
Please let me know where the leak is happening if possible?
Instruments just says Malloc 16 bytes and has about 60 MB worth of these guys...
/*
Parses a dicionary into an object
*/
+(id)makeObject:(id)object fromDictionary:(NSDictionary *)dict{
#autoreleasepool {
NSArray *keys = #[#"#\"NSMutableArray\""];
//Init result
id result = object;
//Iterate every key
for (id key in [dict allKeys]) {
//Convert key to const char
const char * c = [key cStringUsingEncoding:NSASCIIStringEncoding];
//Use c to see if the class has this property
if (class_getProperty([object class], c)) {
//get the property
objc_property_t property = class_getProperty([result class], c);
//Get the property name and type
const char *name = property_getName(property);
const char *type = property_copyAttributeValue(property, "T");
//Cast const char to string
NSString *pNameString = [NSString stringWithFormat:#"%s",name];
NSString *typeString = [NSString stringWithFormat:#"%s",type];
//Add relationships
if ([keys containsObject:typeString]) {
//Get array of objects
NSArray *relationship = [dict objectForKey:pNameString];
NSMutableArray *allSubObjects = [[NSMutableArray alloc]init];
//Parse each individual object
for (NSDictionary *relationshipObj in relationship) {
//Create class from relationship
Class class = NSClassFromString(pNameString);
//Create object
id sub = [self makeObject:[[class alloc]init] fromDictionary:relationshipObj];
[allSubObjects addObject:sub];
}
[result setValue:allSubObjects forKey:pNameString];
}else{
//If so set the property for the key
[result setValue:[dict objectForKey:key] forKey:key];
}
}else{
//NSLog(#"%# did not respond to : %#", result, key);
}
}
//Return result
return result;
}
}
EDIT:
Instruments shows these two items most likely the culprits. What's the best way to fix this?
//Cast const char to string
NSString *pNameString = [NSString stringWithFormat:#"%s",name];
NSString *typeString = [NSString stringWithFormat:#"%s",type];
1) [NSString cStringUsingEncoding]
This allocate some memory internally which will not freed until the NSString object is deallocated. I don't know exactly but could it be possible repeatedly sending cStringUsingEncoding incur unused memory building up?
2) property_copyAttributeValue
Documentation says,
Return Value A C array of pointers of type objc_property_t describing
the properties declared by proto. Any properties declared by other
protocols adopted by this protocol are not included. The array
contains *outCount pointers followed by a NULL terminator. You must
free the array with free().
You need to free const char *type.
3) NSMutableArray *allSubObjects = [[NSMutableArray alloc]init]
It should be NSMutableArray *allSubObjects = [NSMutableArray array].
4) Object creation
Class class = NSClassFromString(pNameString);
//Create object
id sub = [self makeObject:[[class alloc]init] fromDictionary:relationshipObj];
I'd put this code block in its own autorelease pool.
[class alloc]init] is also suspicious.

UIAutomation testing with Cocos2d, possible?

Is it possible to use UIAutomation with cocos2d or any opengl application for that matter?
Specifically I want to use the zucchini framework to test my cocos2d game but that just uses UIAutomation anyway.
You can create custom steps in Zucchini and specify the coordinates to tap, e.g.
'Choose the red bird' : ->
target.tap({x:278, y:36})
'Press Fire' : ->
target.tap({x:170, y:260})
So, I got started with calabash-iOS and expanding on its backdoor. This is just for starters but with this you can get the accessibility label of the current CCScene, so you can check what screen is currently on and thus use for scripting actions. I'm not used to working with objc runtime, but as you can see it's possible to get properties, methods etc. A bit more digging and it should be possible to wrap more functionality, and hopefully something to wrap the cocos2d CCNode structure as well. This is a work in progress.
To use this you need to install https://github.com/calabash/calabash-ios and then implement the below function in the app delegate. Don't forget to set .accessibilityLabel to something like #"menu", #"game" or similar in your code. Optimally, only for the *-cal target, you don't want this code in production builds.
-(NSString*)calabashBackdoor:(NSString*)aIgnorable {
DLog(#"calabashBackdoor: %#", aIgnorable);
// UIApplication* app = [UIApplication sharedApplication];
if (aIgnorable != nil) {
NSArray* p = [aIgnorable componentsSeparatedByString:#" "];
NSString* command = [p objectAtIndex:0];
if ([command isEqualToString:#"getCurrentSceneLabel"]) {
CCDirector* director = [CCDirector sharedDirector];
DLog(#"director.runningScene.accessibilityLabel: %#", director.runningScene.accessibilityLabel);
return director.runningScene.accessibilityLabel;
}
else if ([command isEqualToString:#"class_copyMethodList"]) {
CCDirector* director = [CCDirector sharedDirector];
id inspectThisObject = director.runningScene;
DLog(#"inspectThisObject: %#, %#", [inspectThisObject class], inspectThisObject);
unsigned int count;
// To get the class methods of a class, use class_copyMethodList(object_getClass(cls), &count).
Method* methods = class_copyMethodList(object_getClass(inspectThisObject), &count);
//NSMutableString* returnstring = [NSMutableString string];
NSMutableArray* arrayOfMethodnames = [NSMutableArray array];
if (methods != NULL) {
for (int i = 0; i < count; i++) {
Method method = methods[i];
NSString* stringMethod = NSStringFromSelector(method_getName(method)); //NSStringFromSelector(method->method_name);
[arrayOfMethodnames addObject:stringMethod];
}
// An array of pointers of type Method describing the instance methods implemented by the class—any instance methods implemented by superclasses are not included. The array contains *outCount pointers followed by a NULL terminator. You must free the array with free().
free(methods);
}
DLog(#"arrayOfMethodnames: %#", arrayOfMethodnames);
return [arrayOfMethodnames componentsJoinedByString:#","];
}
else if ([command isEqualToString:#"class_copyPropertyList"]) {
CCDirector* director = [CCDirector sharedDirector];
id inspectThisObject = director.runningScene;
DLog(#"inspectThisObject: %#, %#", [inspectThisObject class], inspectThisObject);
unsigned int count;
// An array of pointers of type objc_property_t describing the properties declared by the class. Any properties declared by superclasses are not included. The array contains *outCount pointers followed by a NULL terminator. You must free the array with free().
//
// If cls declares no properties, or cls is Nil, returns NULL and *outCount is 0.
//
objc_property_t* properties = class_copyPropertyList(object_getClass(inspectThisObject), &count);
NSMutableArray* arrayOfProperties = [NSMutableArray array];
if (properties != NULL) {
for (int i = 0; i < count; i++) {
objc_property_t property = properties[i];
const char* CCS = property_getName(property);
NSString* str = [NSString stringWithUTF8String:CCS];
[arrayOfProperties addObject:str];
}
free(properties);
}
DLog(#"arrayOfProperties: %#", arrayOfProperties);
return [arrayOfProperties componentsJoinedByString:#","];
}
else {
DLog(#"Unhandled command: %#", command);
}
}
return #"calabashBackdoor nil!";
}
In Prefix.pch put this
#ifdef DEBUG
# define DLog( s, ... ) NSLog( #"<%p %#:(%d)> %#", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
# define DLog(...) /* */
#endif
#define ALog(...) NSLog(__VA_ARGS__)
When you get calabash-ios and running, add this in step_definitions/somesteps.rb:
Then(/^I backdoor (.+)$/) do |x|
backdoor("calabashBackdoor:", x)
end