Size of NiceCandidate of libnice

I try to get the Nim version of simple-example.c working for a customer from Japan.

Libnice is hard for language bindings for various reasons.

My current issue is understanding the NiceCandidate struct:

https://libnice.freedesktop.org/libnice/NiceCandidate.html

Of course I know about padding and alignment in structs.

My estimation for the size on a 64 bit Linux box was 128 byte. But sizeof() from gcc tells me 144, which is 16 bytes more. So I hacked this C code together:

// gcc -o t t.c `pkg-config --cflags --libs nice`
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <agent.h>
#include <gio/gnetworking.h>

int main() {

  NiceCandidate n;

  printf("%ld\n", sizeof(NiceCandidate));
  printf("%ld\n", sizeof(n));
  printf("%ld\n", (void *)&(n.password) - (void *)(&n));

  return 0;
}

Output is

$ ./t
144
144
120

So sizeof() gives 144, but offset of last field seems to be 120. Last field is a 8 byte pointer, so total size should be 128. And 128 is 8 * 16, so no reason for padding. It is strange. May the reason is that NiceCandidate is a Boxed type, so a hidden field? I don’t think so, from the header file it is just a struct. What is special is that it contains two fields of type NiceAddress which is a struct containing a union of size 28 bytes each.

If you look at the header, the last field is sockptr and not password:

struct _NiceCandidate
{
  NiceCandidateType type;
  NiceCandidateTransport transport;
  NiceAddress addr;
  NiceAddress base_addr;
  guint32 priority;
  guint stream_id;
  guint component_id;
  gchar foundation[NICE_CANDIDATE_MAX_FOUNDATION];
  gchar *username;        /* pointer to a nul-terminated username string */
  gchar *password;        /* pointer to a nul-terminated password string */
  TurnServer *turn;
  gpointer sockptr;
};

So there are 3 more pointers, bringing you from 120 bytes to 144 bytes in your case.

Generally, getting the sizes of structs right from the .gir files is rather tricky though. Sometimes fields are parsed wrongly, sometimes there are bitfields that need to be handled correctly, and you have to handle padding/alignment correctly according to the C rules for your specific platform.

Without language support that’s a bit of work, I hope Nim has the ability to define structs according to the platform’s C ABI :slight_smile:

1 Like

Thanks.

You made me feel like a moron, my only excuse was that I was a bit tired yesterday. But look at

$ grep -A16 _NiceCandidate /home/salewski/libnice/agent/candidate.h
typedef struct _NiceCandidate NiceCandidate;


/**
 * NiceCandidate:
 * @type: The type of candidate
 * @transport: The transport being used for the candidate
 * @addr: The #NiceAddress of the candidate
 * @base_addr: The #NiceAddress of the base address used by the candidate
 * @priority: The priority of the candidate <emphasis> see note </emphasis>
 * @stream_id: The ID of the stream to which belongs the candidate
 * @component_id: The ID of the component to which belongs the candidate
 * @foundation: The foundation of the candidate
 * @username: The candidate-specific username to use (overrides the one set
 * by nice_agent_set_local_credentials() or nice_agent_set_remote_credentials())
 * @password: The candidate-specific password to use (overrides the one set
 * by nice_agent_set_local_credentials() or nice_agent_set_remote_credentials())
--
struct _NiceCandidate
{
  NiceCandidateType type;
  NiceCandidateTransport transport;
  NiceAddress addr;
  NiceAddress base_addr;
  guint32 priority;
  guint stream_id;
  guint component_id;
  gchar foundation[NICE_CANDIDATE_MAX_FOUNDATION];
  gchar *username;        /* pointer to a nul-terminated username string */
  gchar *password;        /* pointer to a nul-terminated password string */
};

And that libnice I recently got directly from gitlab sources. So my gcc compiler seems to use a different header file? But OK, that should not be a real problem, I just tried to understand it.

The main problem about NiceCandidate struct for gobject-introspection is that there are no getters or setters for the fields defined, and NiceAdress is a struct which contains a union, but it is reported as a struct with no fields by gir. But we have to know size of NiceAdress (28 bytes) to access the fields below it.

Nim supports all that well, for NiceAdress we have already this solution

import nativesockets

type
  # See https://gitlab.freedesktop.org/libnice/libnice/-/blob/master/agent/address.h
  Address* {.union.} = object
    `addr`*: SockAddr
    ip4*: Sockaddr_in
    ip6*: Sockaddr_in6

I think we do not really need the fields of NiceAgent, but we need its correct size.

Yes, apparently libnice recently broke ABI in git and removed the fields after password from NiceCandidate.

See here and here.

That’s because it contains types that gir can’t handle (the union and the struct sockaddrs). You need to work around it manually on your side if you need to access those fields.

Ideally libnice would add GObject properties or accessor functions for the fields as field access is always a bit complicated for non-C-languages, as you found out here.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.