简体   繁体   中英

Can a packed C structure and function be ported to java?

In the past I have written code which handles incoming data from a serial port. The data has a fixed format. Now I want to migrate this code to java (android). However, I see many obstacles.

The actual code is more complex, but I have a simplified version here:

#define byte unsigned char
#define word unsigned short

#pragma pack(1);
struct addr_t
{
  byte foo;
  word bar;
};
#pragma pack();


bool RxData( byte val )
{
  static byte buffer[20];
  static int  idx = 0;

  buffer[idx++] = val;

  return ( idx == sizeof(addr_t) );
}  

The RxData function is called everytime a byte is received. When the complete chunk of data is in, it returns true.

Some of the obstacles:

The used data types are not available to java. In other threads it is recommended to use larger datatypes, but in this case this is not a workable solution.

The size of the structure is in this case exactly 3 bytes. That's also why the #pragma statement is important. Otherwise the C compiler might "optimize" it for memory use, with a different size as a result.

Java also doesn't have a sizeof function and I have found no alternative for this kind of situation.

I could replace the 'sizeof' with a fixed value of 3, but that would be very bad practice IMO.

Is it at all possible to write such a code in java? Or is it wiser to try to add native c source into Android Studio?

Your C code has its problems too. Technically, you do not know how big a char and a short is. You probably want uint8_t and uint16_t respectively. Also, I'm not sure how portable packing is.

In Java, you need a class. The class might as well tell you how many bytes you need to initialise it.

class Addr
{
    private byte foo;
    private short bar;
    public final static int bufferBytes = 3;

    public int getUnsignedFoo()
    {
        return (int)foo & 0xff;
    }
    public int getUnsignedBar()
    {
        return (int)bar & 0xffff;
    }
}

Probably a class for the buffer too although there may already be a suitable class in the standard library.

class Buffer
{
    private final static int maxSize = 20;

    private byte[] bytes = new byte[maxSize];
    private int idx = 0;
    private bool rxData(byte b)
    {
        bytes[idx++] = b;
        return idx == Addr.bufferBytes;
    }
}

To answer the question about the hardcodedness of the 3, this is actually the better way to do it because your the specification of your protocol should say "one byte for foo and two bytes for bar" not "a packed C struct with a char and a short in it". One way to deserialise the buffer is like this:

 public class Addr
 {
     // All the stuff from above
     public Addr(byte[] buffer)
     {
         foo = buffer[0];
         bar = someFunctionThatGetsTheEndiannessRight(buffer[1], buffer[2]);
     }
 }

TI have left the way bar is calculated deliberately vague because it depends on your platform as much as anything. You can do it simply with bit shifts eg

    (((short)buffer[1] & 0xff) << 8) | ((short)buffer[2] & 0xff)

However, there are better options available. For example, you can use a java.nio.ByteBuffer which has the machinery to cope with endian isssues.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM