2010-09-10 2 views
4

Я хочу упаковать MIDI-сообщение в объект NSData.Упаковка в байты (NSData)

int messageType = 3; // 0-15 
int channel = 5;  // 0-15 
int data1 = 56;  // 0-127 
int data2 = 78;  // 0-127 

int packed = data2; 
packed += data1 * 127; 
packed += channel * 16129; // 127^2 
packed += messageType * 258064; // 127^2 * 16 

NSLog(@"packed %d", packed); 

NSData *packedData = [NSData dataWithBytes:&packed length:sizeof(packed)]; 

int recovered; 
[packedData getBytes:&recovered]; 

NSLog(@"recovered %d", recovered); 

Это прекрасно работает и в то время как я горжусь собой, я знаю, что преобразование в байты не будет сделано правильно: это должно быть прямым преобразованием без большого количества операций сложения и умножения. Как это можно сделать?

Edit: Теперь я знаю, что я могу просто сделать это

char theBytes[] = {messageType, channel, data1, data2}; 
NSData *packedData = [NSData dataWithBytes:&theBytes length:sizeof(theBytes)]; 

и на стороне Java

byte[] byteBuffer = new byte[4]; // Receive buffer 
while (in.read(byteBuffer) != -1) { 
    System.out.println("data2=" + byteBuffer[3]); 
} 

и он будет работать, но я бы хотел, решение получить NSData всего за 3 байта.

ответ

2

Вот 3-байтовое решение, которое я собрал вместе.

char theBytes[] = {message_type * 16 + channel, data1, data2}; 
NSData *packedData = [NSData dataWithBytes:&theBytes length:sizeof(theBytes)]; 

char theBytesRecovered[3]; 
[packedData getBytes:theBytesRecovered]; 

int messageTypeAgain = (int)theBytesRecovered[0]/16; 
int channelAgain = (int)theBytesRecovered[0] % 16; 
int data1Again = (int)theBytesRecovered[1]; 
int data2Again = (int)theBytesRecovered[2]; 

NSLog(@"packed %d %d %d %d", messageTypeAgain, channelAgain, data1Again, data2Again); 

, а с другой стороны провода, это так же легко подобрать, потому что каждый байт байт. Я только что попробовал это на стороне iOS и на стороне Java, и никаких проблем нет. Нет проблем с endian-ness, потому что каждое целое число вписывается в один байт (или два в один байт, в одном случае).

+0

там * * проблема с контентом в вашем примере. проходящей через const void *, когда длина> 1 требует рассмотрения в конце. значения массива char будут переупорядочиваться, когда встречаются устройства с различной степенью точности. – justin

+0

Спасибо @Justin. Я не согласен, но я задам еще один вопрос здесь, на короткое время, чтобы прояснить это несоответствие. Я должен попросить его в удобное время суток, чтобы мы получили некоторые мнения экспертов. –

+0

Спасибо @ Justin, я задал вопрос здесь http://stackoverflow.com/questions/3710669/ints-to-bytes-endianess-a-concern –

3

Лично я бы пойти на NSString:

NSString *dataString = [NSString stringWithFormat:@"%i+%i+%i+%i", messageType, channel, data1, data2]; 
NSData *packedData = [dataString dataUsingEncoding:NSUTF8StringEncoding]; 

Простой в использовании и легко переносить. Распаковка немного сложнее, но совсем не сложно.

NSScanner *scanner = [NSScanner scannerWithString:[[[NSString alloc] initWithData:packedData encoding:NSUTF8StringEncoding] autorelease]]; 
int messageType, channel, data1, data2; 
[scanner scanInt:&messageType]; 
[scanner scanInt:&channel]; 
[scanner scanInt:&data1]; 
[scanner scanInt:&data2]; 
+0

это выглядит прекрасно (не знаю о NSScanner, это мило), но только в этом крошечном фрагменте кода я бы ЛЮБЛЮ, чтобы избежать создания дополнительных объектов, так как я мог обрабатывать тонны сообщений сразу на iOS. Кроме того, и очень важно: данные будут отправлены на другой компьютер, поэтому реализация строки была исключена. –

+0

@Yar, я вижу, что вы говорите о создании экземпляра «NSScanner», хотя я не знаю, насколько он эффективен. Однако об 'NSString': они действительно портативны. У вас не должно возникнуть проблем с отправкой их через сеть. Еще лучше: упакован как 'NSData', вам должно быть хорошо идти ... –

+0

@Yar Я бы сказал, чтобы он работал (т. Е. Работал с любым видом общения), а затем анализировал ваш код и оптимизировал его, если анализ показывает что у вас есть особые узкие места. Вопрос кажется, что это случай преждевременной оптимизации. (хотя, возможно, вы уже обнаружили, что это узкое место, я не знаю) –

0

У вас есть несколько вариантов.

, так как он выглядит, как вы хотите, смежную Glob данных в представлении NSData ...

вы хотите создать упакованную-структуру, и передавать данные на вызов NSData как предопределенные байты (так оба конца знают, как разблокировать данные glob).

/* pack this struct's ivars and and enable -Wreorder to sanity check that the compiler does not reorder members -- i see no reason for the compiler to do this since the fields are equal size/type */ 
struct t_midi_message { 
    UInt8 message_type; /* 0-15 */ 
    UInt8 channel; /* 0-15 */ 
    UInt8 data1; /* 0-127 */ 
    UInt8 data2; /* 0-127 */ 
}; 

union t_midi_message_archive { 
/* members - as a union for easy endian swapping */ 
    SInt32 glob; 
    t_midi_message message; 
    enum { ValidateSize = 1/(4 == sizeof(t_midi_message)) }; 
/* nothing unusual here, although you may want a ctor which takes NSData as an argument */ 
    t_midi_message_archive(); 
    t_midi_message_archive(const t_midi_message&); 
    t_midi_message_archive(const t_midi_message_archive&); 
    t_midi_message_archive& operator=(const t_midi_message_archive&); 

/* swap routines -- just pass @member glob to the system's endian routines */ 
    void swapToNativeEndianFromTransferEndian(); 
    void swapToTransferEndianFromNativeEndian(); 

}; 

void a(const t_midi_message_archive& msg) { 

    t_midi_message_archive copy(msg); 
    copy.swapToTransferEndianFromNativeEndian(); 

    NSData * packedData([NSData dataWithBytes:&copy.glob length:sizeof(copy.glob)]); 
    assert(packedData); 

    t_midi_message_archive recovered; 
    [packedData getBytes:&recovered.glob]; 

    recovered.swapToNativeEndianFromTransferEndian(); 
    /* recovered may now be used safely */ 
} 
+0

Извините, может быть, я не был ясен: мне не нужен большой глобус данных в NSData. Я хотел бы получить информацию о 3 байтах в NSData любым способом. Я не беспокоюсь о том, что сообщения очень мелкие внутри iOS, но я бы хотел, чтобы байты, лежащие в основе NSData, были равны 3. –

+0

i count 4 - компиляция будет залогом, если: enum {ValidateSize = 1/(4 ==) sizeof (t_midi_message))}; false. EDIT: нормально, вы можете уменьшить его до 3 - что кажется ненужной микро-оптимизацией. – justin

+0

Если вы действительно хотите 3 вместо 4, вы просто используете битполы в t_midi_message. – justin