Delphi convert from c# - c#

i really need your help to port this c# code to Delphi one :
public unsafe byte[] Encode(byte[] inputPcmSamples, int sampleLength, out int encodedLength)
{
if (disposed)
throw new ObjectDisposedException("OpusEncoder");
int frames = FrameCount(inputPcmSamples);
IntPtr encodedPtr;
byte[] encoded =new byte [MaxDataBytes];
int length = 0;
/* How this can be ported to delphi */
fixed (byte* benc = encoded)
{
encodedPtr = new IntPtr((void*)benc);
length = API.opus_encode(_encoder, inputPcmSamples, frames, encodedPtr, sampleLength);
}
encodedLength = length;
if (length < 0)
throw new Exception("Encoding failed - " + ((Errors)length).ToString());
return encoded;
}
The main code part that i'm looking for is :
fixed (byte* benc = encoded)
{
encodedPtr = new IntPtr((void*)benc);
/* API.opus_encode = is declared in an other Class */
length = API.opus_encode(_encoder, inputPcmSamples, frames, encodedPtr, sampleLength);
}
many thanks

You seem to want to know how to deal with the fixed block in the C#.
byte[] encoded =new byte [MaxDataBytes];
....
fixed (byte* benc = encoded)
{
encodedPtr = new IntPtr((void*)benc);
length = API.opus_encode(_encoder, inputPcmSamples, frames, encodedPtr, sampleLength);
}
This use of fixed is to pin the managed array to obtain a pointer to be passed to the unmanaged code.
In Delphi we just want an array of bytes, and a pointer to that array. That would look like this:
var
encoded: TBytes;
....
SetLength(encoded, MaxDataBytes);
....
length := opus_encode(..., Pointer(encoded), ...);
or perhaps:
length := opus_encode(..., PByte(encoded), ...);
or perhaps:
length := opus_encode(..., #encoded[0], ...);
depending on how you declared the imported function opus_encode and your preferences.
If MaxDataBytes was a small enough value for the buffer to live on the stack, and MaxDataBytes was known at compile time, then a fixed length array could be used.

Related

Bytes sequence problem during Marshaling in C#

I want to use my laptop to communicate with MES(Manufacturing Execution System).
And when I serialized the data (struct type), something happen.
The code below is what I have done:
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct DataPackage
{
public int a;
public ushort b;
public byte c;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)] public string d;
}
class Program
{
static void Main(string[] args)
{
DataPackage pack1 = new DataPackage();
pack1.a = 0x33333301;
pack1.b = 200;
pack1.c = 21;
pack1.d = "hello";
byte[] pack1_serialized = getBytes(pack1);
Console.WriteLine(BitConverter.ToString(pack1_serialized));
byte[] getBytes(DataPackage str)
{
int size = Marshal.SizeOf(str);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(str, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
}
}
And here is the outcome:
I want the outcome to be like this:
33-33-33-01-00-C8-15-68-65-6C-6C-6F
So the questions are:
Why is the uint / ushort type data reverse after Marshalling?
Is there any other way that I can send the data in the sequence that I want ?
Why is the last word "o" in string "hello" disappear in the byte array ?
Thanks.
1 - Because your expected outcome is big endian, and your system appears to use little endian, so basically reversed order of bytes compared to what you expect.
2- Easiest way is to "convert" your numbers to big endian before marshalling (that is change them in a way which will produce desired result while converting them using little endian), for example like this:
static int ToBigEndianInt(int x) {
if (!BitConverter.IsLittleEndian)
return x; // already fine
var ar = BitConverter.GetBytes(x);
Array.Reverse(ar);
return BitConverter.ToInt32(ar, 0);
}
static ushort ToBigEndianShort(ushort x) {
if (!BitConverter.IsLittleEndian)
return x; // already fine
var ar = BitConverter.GetBytes(x);
Array.Reverse(ar);
return BitConverter.ToUInt16(ar, 0);
}
And then:
pack1.a = ToBigEndianInt(0x33333301);
pack1.b = ToBigEndianShort(200);
Note that this way of conversion is not very efficient and if you need more perfomance you can do this with some bit manipulations.
3 - Because string is null terminated, and this null terminator counts in SizeConst. Since you have it 5, there will be 4 characters of your string + 1 null terminator. Just increase SizeConst = 6 (that might add additional zeroes at the end because of Pack = 4).

Unsafe conversion to given pointer in buffer

I am trying to figure out how to get this method to start at a index being ptr instead of overwriting from 0 - 8 bytes. Also if anyone knows how to make this work with Big / Little edian machines due to being networked, that would be awesome.
private byte[] buffer = new byte[30];
private int ptr = 0;
unsafe void GetBytes(ulong value)
{
fixed (byte* b = buffer) //start at buffer[ptr]
*((int*)b) = *(int*)&value;
ptr += 8;
}
I figured out how to make a pointer to the ptr via the following
private byte[] buffer = new byte[30];
private int ptr = 0;
unsafe void GetBytes(ulong value)
{
fixed (byte* b = &buffer[ptr]) //start at buffer[ptr]
*((int*)b) = *(int*)&value;
ptr += 8;
}
You can convert endianness easily:
using System.Buffers.Binary;
if (!BitConverter.IsLittleEndian)
{
value = BinaryPrimitives.ReverseEndianness(value);
}
I would prefer to use Span which will be safer and easier to read:
using System.Buffers.Binary;
Span<byte> span = buffer;
BinaryPrimitives.WriteUInt64LittleEndian(span.Slice(ptr), value);
If you want to use your system's endianness rather than something explicit, you can do that too:
using System.Runtime.InteropServices;
Span<byte> span = buffer;
MemoryMarshal.Write(span.Slice(ptr), value);
Edit for non-Core:
You can use HostToNetworkOrder to convert integers to big endian prior to writing to your pointer.

passing c++ char* to c# via shared-memory

Sorry for probably simple question but I'm newbie in shared memory and trying to learn by example how to do things.
On c++ side I receive such pair: const unsigned char * value, size_t length
On c# side I need to have regular c# string. Using shared memory what is the best way to do that?
It's not that easy to using the string.
If it's me, I'll try these ways:
1.simply get a copy of the string. System.Text.Encoding.Default.GetString may convert from a byte array to a string.
You may try in a unsafe code block (for that you could use pointer type) to do:
(1) create a byte array, size is your "length"
byte[] buf = new byte[length];
(2) copy your data to the array
for(int i = 0; i < length; ++i) buf[i] = value[i];
(3) get the string
string what_you_want = System.Text.Encoding.Default.GetString(buf);
2.write a class, having a property "string what_you_want", and each time you access it, the above process will perform.
before all, you should first using P/Invoke feature to get the value of that pair.
edit: this is an example.
C++ code:
struct Pair {
int length;
unsigned char value[1024];
};
#include <windows.h>
#include <stdio.h>
int main()
{
const char* s = "hahaha";
HANDLE handle = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(Pair), L"MySharedMemory");
struct Pair* p = (struct Pair*) MapViewOfFile(handle, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(Pair));
if (p != 0) {
p->length = lstrlenA(s);
lstrcpyA((char*)p->value, s);
puts("plz start c# program");
getchar();
} else
puts("create shared memory error");
if (handle != NULL)
CloseHandle(handle);
return 0;
}
and C# code:
using System;
using System.IO.MemoryMappedFiles;
class Program
{
static void Main(string[] args)
{
MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("MySharedMemory");
MemoryMappedViewStream mmfvs = mmf.CreateViewStream();
byte[] blen = new byte[4];
mmfvs.Read(blen, 0, 4);
int len = blen[0] + blen[1] * 256 + blen[2] * 65536 + blen[3] * 16777216;
byte[] strbuf = new byte[len];
mmfvs.Read(strbuf, 0, len);
string s = System.Text.Encoding.Default.GetString(strbuf);
Console.WriteLine(s);
}
}
just for example.
you may also add error-check part.

C# Copy variables into buffer without creating garbage?

Is it possible in C# .Net (3.5 and above) to copy a variable into a byte[] buffer without creating any garbage in the process?
For instance:
int variableToCopy = 9861;
byte[] buffer = new byte[1024];
byte[] bytes = BitConverter.GetBytes(variableToCopy);
Buffer.BlockCopy(bytes, 0, buffer, 0, 4);
float anotherVariableToCopy = 6743897.6377f;
bytes = BitConverter.GetBytes(anotherVariableToCopy);
Buffer.BlockCopy(bytes, 0, buffer, 4, sizeof(float));
...
creates the byte[] bytes intermediary object which becomes garbage (presuming a ref is no longer held to it)...
I wonder if using bitwise operators the variable can be copied directly into the buffer without creating the intermediary byte[]?
Use pointers is the best and the fastest way:
You can do this with any number of variables, there is no wasted memory, the fixed statement has a little overhead but it's too small
int v1 = 123;
float v2 = 253F;
byte[] buffer = new byte[1024];
fixed (byte* pbuffer = buffer)
{
//v1 is stored on the first 4 bytes of the buffer:
byte* scan = pbuffer;
*(int*)(scan) = v1;
scan += 4; //4 bytes per int
//v2 is stored on the second 4 bytes of the buffer:
*(float*)(scan) = v2;
scan += 4; //4 bytes per float
}
Why can't you just do:
byte[] buffer = BitConverter.GetBytes(variableToCopy);
Note that the array here is not an indirection into the storage for the original Int32, it is very much a copy.
You are perhaps worried that bytes in your example is equivalent to:
unsafe
{
byte* bytes = (byte*) &variableToCopy;
}
.. but I assure you that it is not; it is a byte by byte copy of the bytes in the source Int32.
EDIT:
Based on your edit, I think you want something like this (requires unsafe context):
public unsafe static void CopyBytes(int value, byte[] destination, int offset)
{
if (destination == null)
throw new ArgumentNullException("destination");
if (offset < 0 || (offset + sizeof(int) > destination.Length))
throw new ArgumentOutOfRangeException("offset");
fixed (byte* ptrToStart = destination)
{
*(int*)(ptrToStart + offset) = value;
}
}

Sending an image from a C# client to a C server

If I send plain text there is no problem. Everything is ok.
However If I try to send from the C# client an image, the server receives correct bytes number, but when I save the buffer to a file (in binary mode - wb), it always has 4 bytes.
I send it by the C# client by using the function File.ReadAllBytes().
My saving code looks like
FILE * pFile;
char *buf = ReceiveMessage(s);
pFile = fopen (fileName , "wb");
fwrite(buf, sizeof(buf[0]), sizeof(buf)/sizeof(buf[0]), pFile);
fclose (pFile);
free(buf);
My receiving function looks like
static unsigned char *ReceiveMessage(int s)
{
int prefix;
recv(s, &prefix, 4, 0);
int len = prefix;
char *buffer= (char*)malloc(len + 1);
int received = 0, totalReceived = 0;
buffer[len] = '\0';
while (totalReceived < len)
{
if (len - totalReceived > BUFFER_SIZE)
{
received = recv(s, buffer + totalReceived, BUFFER_SIZE, 0);
}
else
{
received = recv(s, buffer + totalReceived, len - totalReceived, 0);
}
totalReceived += received;
}
return buffer;
}
Your C code needs to pass len back from the ReceiveMessage() function.
char *buf = ReceiveMessage(s); // buf is a char*
... sizeof(buff) // sizeof(char*) is 4 or 8
So you'll need something like
static unsigned char *ReceiveMessage(int s, int* lenOut)
{
...
*lenOut = totalReceived ;
}
You do a beginners mistake of using sizeof(buf). It doesn't return the number of bytes in the buffer but the size of the pointer (which is four or eight depending on if you run 32 or 64 bit platform).
You need to change the ReceiveMessage function to also "return" the size of the received data.
You do not get size of array by sizeof. Change to i.e.:
int len = 0;
char *buf;
buf = ReceiveMessage(s, &len);
/* then use len to calculate write length */
static unsigned char *ReceiveMessage(int s, int *len)
/* or return len and pass ptr to buf */
{
...
}

Categories

Resources