Files
transshift/TransmissionRPCClient/Bencoding.m
2015-07-26 17:55:11 +03:00

160 lines
3.7 KiB
Objective-C

//
// Bencoding.m
// TransmissionRPCClient
//
// Created by Alexey Chechetkin on 19.07.15.
// Copyright (c) 2015 Alexey Chechetkin. All rights reserved.
//
#import "Bencoding.h"
#define UTF8_ACCEPT 0
#define UTF8_REJECT 1
#define ISDIGIT(c) ( ((c) >= '0' && (c) <= '9') )
#define DATA_ALWAYS_UTF8 0
static char *p, *p0;
static NSUInteger dataLength;
id decodeNextObject(); // forward declaration
static const uint8_t utf8d[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
};
BOOL isUTF8(char *str, int len)
{
int type, state = UTF8_ACCEPT;
for ( int i = 0; i < len; i++)
{
type = utf8d[(uint8_t)str[i]];
state = utf8d[256 + state * 16 + type];
if (state == UTF8_REJECT)
break;
}
return state == UTF8_ACCEPT;
}
long long decodeInt()
{
p0 = p;
while ( *p != 'e' && *p != ':' ) p++;
*p++ = '\0';
return atoll(p0);
}
id decodeData()
{
// get data length
int count = (int)decodeInt();
// test data if it's a string
if( isUTF8(p, count) )
{
p0 = p;
p += count;
char c = *p;
*p = '\0';
NSString *str = [NSString stringWithCString:p0 encoding: NSUTF8StringEncoding];
*p = c;
return str;
}
NSData *data = [NSData dataWithBytes:p length:count];
p += count;
return data;
}
NSString* decodeString()
{
int count = (int)decodeInt();
p0 = p;
p += count;
char c = *p;
*p = '\0';
NSString *str = [NSString stringWithCString:p0 encoding: NSUTF8StringEncoding];
*p = c;
return str;
}
NSArray* decodeArray()
{
NSMutableArray *list = [NSMutableArray array];
while ( *p != 'e' )
[list addObject:decodeNextObject()];
p++;
return list;
}
NSDictionary *decodeDictionary()
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
while ( *p != 'e' )
dict[ decodeString() ] = decodeNextObject();
p++;
return dict;
}
id decodeNextObject()
{
char c = *p++;
if( c == 'i' )
return @(decodeInt());
if( c == 'l' )
return decodeArray();
if( c == 'd' )
return decodeDictionary();
if( ISDIGIT(c) )
{
p--;
return DATA_ALWAYS_UTF8 ? decodeString() : decodeData();
}
return nil;
}
id decodeObjectFromBencodedData(NSData *data)
{
// alloc buffer for duplicated data
dataLength = data.length;
char *mp = malloc(dataLength);
p = mp;
[data getBytes:p length:dataLength];
id obj = decodeNextObject();
free(mp);
return obj;
}