How to read a NSInputStream with UTF-8? - ios

I try to read a large file in iOS using NSInputStream to separate the files line by newlines (I don't want to use componentsSeparatedByCharactersInSet as it uses too much memory).
But as not all lines seem to be UTF-8 encoded (as they can appear just as ASCII, same bytes) I often get the Incorrect NSStringEncoding value 0x0000 detected. Assuming NSASCIIStringEncoding. Will stop this compatiblity mapping behavior in the near future. warning.
My question is: Is there a way to surpress this warning by e.g. setting a compiler flag?
Furthermore: Is it save to append/concatenate two buffer reads, as reading from the byte stream, then converting the buffer to string and then appending the string could make the string corrupted?
Below an example method that demonstrates that the byte to string conversion will discard the first and second half of the UTF-8 character, as being invalid.
- (void)NSInputStreamTest {
uint8_t testString[] = {0xd0, 0x91}; // #"Б"
// Test 1: Read max 1 byte at a time of UTF-8 string
uint8_t buf1[1], buf2[1];
NSString *s1, *s2, *s3;
NSInteger c1, c2;
NSInputStream *inStream = [[NSInputStream alloc] initWithData:[[NSData alloc] initWithBytes:testString length:2]];
[inStream open];
c1 = [inStream read:buf1 maxLength:1];
s1 = [[NSString alloc] initWithBytes:buf1 length:1 encoding:NSUTF8StringEncoding];
NSLog(#"Test 1: Read %d byte(s): %#", c1, s1);
c2 = [inStream read:buf2 maxLength:1];
s2 = [[NSString alloc] initWithBytes:buf2 length:1 encoding:NSUTF8StringEncoding];
NSLog(#"Test 1: Read %d byte(s): %#", c2, s2);
s3 = [s1 stringByAppendingString:s2];
NSLog(#"Test 1: Concatenated: %#", s3);
[inStream close];
// Test 2: Read max 2 bytes at a time of UTF-8 string
uint8_t buf4[2];
NSString *s4;
NSInteger c4;
NSInputStream *inStream2 = [[NSInputStream alloc] initWithData:[[NSData alloc] initWithBytes:testString length:2]];
[inStream2 open];
c4 = [inStream2 read:buf4 maxLength:2];
s4 = [[NSString alloc] initWithBytes:buf4 length:2 encoding:NSUTF8StringEncoding];
NSLog(#"Test 2: Read %d byte(s): %#", c4, s4);
[inStream2 close];
}
Output:
2013-02-10 21:16:23.412 Test[11144:c07] Test 1: Read 1 byte(s): (null)
2013-02-10 21:16:23.413 Test[11144:c07] Test 1: Read 1 byte(s): (null)
2013-02-10 21:16:23.413 Test[11144:c07] Test 1: Concatenated: (null)
2013-02-10 21:16:23.413 Test[11144:c07] Test 2: Read 2 byte(s): Б

First of all, in line: s3 = [s1 stringByAppendingString:s2]; you are trying to concatenate to 'nil' values. The result would be 'nil' also. So, you may want to concatenate bytes instead of strings:
uint8_t buf3[2];
buf3[0] = buf1[0];
buf3[1] = buf2[0];
s3 = [[NSString alloc] initWithBytes:buf3 length:2 encoding:NSUTF8StringEncoding];
Output:
2015-11-06 12:57:40.304 Test[10803:883182] Test 1: Read 1 byte(s): (null)
2015-11-06 12:57:40.305 Test[10803:883182] Test 1: Read 1 byte(s): (null)
2015-11-06 12:57:40.305 Test[10803:883182] Test 1: Concatenated: Б
Secondary, length of UTF-8 character may lay in [1..6] bytes.
(1 byte) 0aaa aaaa //if symbol lays in 0x00 .. 0x7F (ASCII)
(2 bytes) 110x xxxx 10xx xxxx
(3 bytes) 1110 xxxx 10xx xxxx 10xx xxxx
(4 bytes) 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
(5 bytes) 1111 10xx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx
(6 bytes) 1111 110x 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx 10xx xxxx
So, if you are intended to read from NSInputStream raw bytes and then translate them into UTF-8 NSString, you probably want to read byte by byte from NSInputStream until you will get valid string:
#define MAX_UTF8_BYTES 6
NSString *utf8String;
NSMutableData *_data = [[NSMutableData alloc] init]; //for easy 'appending' bytes
int bytes_read = 0;
while (!utf8String) {
if (bytes_read > MAX_UTF8_BYTES) {
NSLog(#"Can't decode input byte array into UTF8.");
return;
}
else {
uint8_t byte[1];
[_inputStream read:byte maxLength:1];
[_data appendBytes:byte length:1];
utf8String = [NSString stringWithUTF8String:[_data bytes]];
bytes_read++;
}
}

ASCII (and hence the newline character) is a subset of UTF-8, so there should not be any conflict.
It should be possible to divide your stream at the newline characters, as you would in a simple ASCII stream. Then you can convert each chunk ("line") into an NSString using UTF-8.
Are you sure the encoding errors are not real, i.e., that your stream may actually contain erroneous characters with respect to a UTF-8 encoding?
Edited to add from the comments:
This presumes that the lines consist of sufficiently few characters to keep a whole line in memory before converting from UTF-8.

Related

What is wrong with £ symbol when using it in encryptor?

When I use £ symbol in password according with an AES encryptor I get the error
Key length must be 128/192/256 bits
String pass = 'my_cool_password_£..............';
var key = Key.fromUtf8(pass);
var encrypter = Encrypter(AES(key));
encrypter.encrypt(plainText, iv: iv); // error `Key length must be 128/192/256 bits`
Stack trace
Unhandled exception:
Invalid argument(s): Key length must be 128/192/256 bits
#0 AESFastEngine.init (package:pointycastle/block/aes_fast.dart:66:7)
#1 SICStreamCipher.init (package:pointycastle/stream/sic.dart:55:22)
#2 StreamCipherAsBlockCipher.init (package:pointycastle/adapters/stream_cipher_as_block_cipher.dart:27:18)
#3 PaddedBlockCipherImpl.init (package:pointycastle/padded_block_cipher/padded_block_cipher_impl.dart:43:12)
#4 AES.encrypt (package:encrypt/src/algorithms/aes.dart:19:9)
#5 Encrypter.encryptBytes (package:encrypt/src/encrypter.dart:12:19)
#6 Encrypter.encrypt (package:encrypt/src/encrypter.dart:20:12)
the package was used https://pub.dev/packages/encrypt
here is the package encrypt function
Encrypted encrypt(String input, {IV iv}) {
return encryptBytes(convert.utf8.encode(input), iv: iv);
}
Since you are using UTF-8 to represent your password, you need to take into account that not all letters can be represented with only 1 byte (8 bits).
E.g. the £ is represented by using two bytes (16 bits): c2 a3
This can be seen in the following example:
import 'dart:convert';
void main() {
print(utf8.encode('my_cool_password_£..............').length * 8); // 264
print(utf8.encode('my_cool_password_x..............').length * 8); // 256
print(utf8.encode('£').length * 8); // 16
print(utf8.encode('£').map((i) => i.toRadixString(16))); // (c2, a3)
}

How to appropriately encrypt and decrypt a NSString with AES 128

I am using http://aes.online-domain-tools.com to encrypt my NSString and what i get back from this is an array of unsigned char like this c2 84 6b 71 72 6d d2 e7 cd 0b a6 08 cd 85 c3 0c.
Then is use this to convert it into NSString in my code:
const unsigned char encrpytedAppIDbytes[] = {0xe5, 0x35, 0xdf, 0x72, 0x57, 0xaf, 0xf7, 0xe6, 0x1f, 0x6d, 0x51, 0x1d, 0x26, 0xe8, 0x5e, 0xa2};
NSData *appIDToDecrypt = [NSData dataWithBytes:encrpytedAppIDbytes length:sizeof(encrpytedAppIDbytes)];
NSString *decryptedAppID = [[NSString alloc] initWithData:[appIDToDecrypt AES128DecryptedDataWithKey:#"something"] encoding:NSUTF8StringEncoding];
if([decryptedAppID isEqualToString:#"Something"]){} // This fails even when i look at them in the debugger they are the same.
But when i am trying to decrypt it, its showing up as the same string but when i compare it with the same NSString hardcode to check if it is the same string it doesn't work.
This fails some authentication check i have in my app.
Please point anything wrong i am doing here.
Thanks,
Alright so after spending few hours with it i finally found the solutions which might not be optimal but works in my case.
It seems like after decryption, the string contains some other characters which are not visible in the debugger but when i am trying to check the length it shows greater than the number of characters in it which indicates that there is something wrong. For now what i have done is this :
const unsigned char nameBytes[] = {0xa6, 0xf0, 0xea, 0x36, 0x5f, 0x78, 0xb7, 0x52, 0x29, 0x6a, 0x67, 0xb7, 0xeb, 0x73, 0xd5, 0x14};
NSData *nameBytesData = [NSData dataWithBytes:nameBytes length:sizeof(nameBytes)];
NSString *nameBytesString = [[NSString alloc] initWithData:[nameBytesData AES128DecryptedDataWithKey:#"PaymentGateway"] encoding:NSUTF8StringEncoding];
NSCharacterSet * set = [[NSCharacterSet characterSetWithCharactersInString:#"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLKMNOPQRSTUVWXYZ0123456789"] invertedSet];
NSString *safeSearchString = [[nameBytesString componentsSeparatedByCharactersInSet:set] componentsJoinedByString:#""];
NSLog(#"length:%lu",(unsigned long)[safeSearchString length]);
NSLog(#"lengthActual:%lu",(unsigned long)[#"ashutosh" length]);
if ([safeSearchString isEqualToString:#"ashutosh"]) {
NSLog(#"Success");
}
NSLog(#"Decrypted:%#",nameBytesString);
The code above removes all the special characters and replaces it with #"" so the resulted string only has valid chars. For adding support to consider more chars as valid just add them to NSCharacterSet * set.

IOS:Convert hex values from the characterstic.value result

i am able to retrieve value from the peripheral as hex value and i need to convert as per my requirement.[24/12/14 11:37:00 am] sonali_phatak: I can see that i have received proper response.from 01117100352e36302e313100000000e55a
01 - 01-start byte
11 - 17(Dec) - length of responce packet
71 - response ID
00 - Ignore this byte
So now out of total length 17, first 4 bytes are header, last 2 bytes are CRC. We
need to read remaining 11 bytes and convert them to ASCII.
35 - 5
2e - .
36 - 6
30 - 0
2e - .
31 - 1
31 - 1
So Iam getting version number from watch as 5.60.11
But i need to show the above value 5.60.11 in string and print in console . how to convert it pleas help me
Please try this :
NSString *strOriginalHex= #"01117100352e36302e313100000000e55a";
NSString *strNewHexForVersion = [strOriginalHex substringWithRange:NSMakeRange(8, 14)];
NSLog(#"%#",[self stringFromHexString:strNewHexForVersion]);//5.60.11
- (NSString *)stringFromHexString:(NSString *)aStrHexString
{
// The hex codes should all be two characters.
if (([aStrHexString length] % 2) != 0)
return nil;
NSMutableString *aMutStrNewString = [NSMutableString string];
for (NSInteger i = 0; i < [aStrHexString length]; i += 2)
{
NSString *hex = [aStrHexString substringWithRange:NSMakeRange(i, 2)];
NSInteger decimalValue = 0;
sscanf([hex UTF8String], "%x", &decimalValue);
[aMutStrNewString appendFormat:#"%c", decimalValue];
}
return aMutStrNewString;
}

Set data for each bit in NSData iOS

I created NSData of length 2 bytes (16 bits) and I want to set first 12 bits as binary value of (int)120 and 13th bit as 0 or 1(bit), 14th bit as 0 or 1 (bit) and 15th bit as 0 or 1(bit).
This is what I want to do:
0000 0111 1000 --> 12 bits as 120 (int)
1 <-- 13th bit
0 <-- 14th bit
1 <-- 15th bit
1 <-- 16th bit
Expected output => 0000 0111 1000 1011 : final binary and convert it to NSData.
How can I do that? Please give me some advice. Thanks all.
0000011110001011 in bit is 0x07 0x8b in byte.
unsigned char a[2] ;
a[0] = 0x07 ;
a[1] = 0x8b ;
NSData * d = [NSData dataWithBytes:a length:2] ;
Recently i wrote this code for my own project.. Check if this can be helpful to you.
//Create a NSMutableData with particular num of data bytes
NSMutableData *dataBytes= [[NSMutableData alloc] initWithLength:numberOfDataBytes];
//get the byte in which you want to change bit.
char x;
[dataBytes getBytes:&x range:NSMakeRange(bytePos,1)];
//change the bit here by shift operation
x |= 1<< (bitNum%8);
//put byte back in NSMutableData
[dataBytes replaceBytesInRange:NSMakeRange(bytePos,1) withBytes:&x length:1];
Let me know if you need more help ..:)
For changing 13th bit based on string equality
if([myString isEqualTo:#"hello"])
{
char x;
[dataBytes getBytes:&x range:NSMakeRange(0,1)];
//change the bit here by shift operation
x |= 1<< (4%8); // x |=1<<4;
//put byte back in NSMutableData
[dataBytes replaceBytesInRange:NSMakeRange(0,1) withBytes:&x length:1];
}
This is the exact code you might need:
uint16_t x= 120; //2 byte unsigned int (0000 0000 0111 1000)
//You need 13th bit to be 1
x<<=1; //Shift bits to left .(x= 0000 0000 1111 0000)
x|=1; //OR with 1 (x= 0000 0000 1111 0001)
//14th bit to be 0.
x<<=1; // (x=0000 0001 1110 0010)
//15th bit to be 1
x<<=1; //x= 0000 0011 1100 0100
x|=1; //x= 0000 0011 1100 0101
//16th bit to be 1
x<<=1; //x= 0000 0111 1000 1010
x|=1; //x= 0000 0111 1000 1011
//Now convert x into NSData
/** **** Replace this for Big Endian ***************/
NSMutableData *data = [[NSMutableData alloc] init];
int MSB = x/256;
int LSB = x%256;
[data appendBytes:&MSB length:1];
[data appendBytes:&LSB length:1];
/** **** Replace upto here.. :) ***************/
//replace with :
//NSMutableData *data = [[NSMutableData alloc] initWithBytes:&x length:sizeof(x)];
NSLog(#"%#",[data description]);
Output: <078b> //x= 0000 0111 1000 1011 //for Big Endian : <8b07> x= 1000 1011 0000 0111

read()/write() calls on iOS seem to be limited by 2250 bytes

I am having a strange problem trying to read and write 9k bytes with open(), read() and write(). When I attempt to write 9k to a file and read it back, the data only goes up to 2250 bytes. After that everything is zeros.
Here is my code (except for the filename which isn't relevant, I'm just putting it to NSDocumentDirectory):
int fp = open([appFile cStringUsingEncoding:NSASCIIStringEncoding], O_RDWR | O_CREAT, 0644);
[_detailViewController log:#"first open() returns %i (err: %i)", fp, errno];
int data2[10000];
int data3[10000];
for (int i=0;i<10000;i++) data2[i] = 1;
[_detailViewController log:#"resetting seek to 0"];
int seekPos = lseek(fp, 0, SEEK_SET);
result = write(fp, data2, 9000);
[_detailViewController log:#"wrote 9k, result is %i", result];
[_detailViewController log:#"resetting seek to 0"];
seekPos = lseek(fp, 0, SEEK_SET);
result = read(fp, data3, 9000);
[_detailViewController log:#"read 9k, result is %i", result];
[_detailViewController log:#"values of data2[2248-2252] = 0x%x 0x%x 0x%x 0x%x 0x%x", data2[2248], data2[2249], data2[2250], data2[2251], data2[2252]];
[_detailViewController log:#"values of data3[2248-2252] = 0x%x 0x%x 0x%x 0x%x 0x%x", data3[2248], data3[2249], data3[2250], data3[2251], data3[2252]];
close(fp);
And here is the strange output:
2013-02-13 14:08:38.290 FileTester[2800:907] first open() returns 6 (err: 3)
2013-02-13 14:08:38.295 FileTester[2800:907] resetting seek to 0
2013-02-13 14:08:38.301 FileTester[2800:907] wrote 9k, result is 9000
2013-02-13 14:08:38.306 FileTester[2800:907] resetting seek to 0
2013-02-13 14:08:38.311 FileTester[2800:907] read 9k, result is 9000
2013-02-13 14:08:38.319 FileTester[2800:907] values of data2[2248-2252] = 0x1 0x1 0x1 0x1 0x1
2013-02-13 14:08:38.327 FileTester[2800:907] values of data3[2248-2252] = 0x1 0x1 0x0 0x0 0x0
As you can see on the last line, the data suddenly goes to zero.
Any ideas what I might be doing wrong? The thing that really gets me is that both the read() and write() return 9000.
As mentioned by ughoavgfhw (Thanks!) the problem was I was mixing up bytes and ints. 9000 bytes is the same thing as 2250 ints, since each int is 4 bytes.

Resources