I want to pass a table from Lua to C and then access all the values in C without copying the values from Lua address space to C stack. Is there a way of doing this?? I want to minimize the copy of values.
I tried using gettable() but in that case, the value is copied onto top of stack. So a copy is getting generated. I don't want this. Is there any other way??
Here is my C code:-
#include <lua.h> /* Always include this */
#include <lauxlib.h> /* Always include this */
#include <lualib.h> /* Always include this */
#include <malloc.h>
#define EXCEPTION_IS_NUMBER (-2) //Passed a custom error no. to be returned in
//case of error
#define SUCCESS (0)
static int iquicksort(lua_State *L) {
int k,len=0;
len=lua_tointeger(L,-2); //-2 specifies second element from top of stack.
//So I have passed 2 elements from Lua to C, first
//is size of table and second table. So when they
//are pushed to stack, the size is second element
//from top.So here I am storing it in variable len.
int *q;
int *p=(int *)malloc(len*sizeof(int));
q=p;
for(k=1;k<=len;k++)
{
lua_pushinteger(L,k); //if I want to access a[2], where a is my table
//and 2 is the index, then '2' needs to be at top
//of the stack and I need to pass the location of
//'a' in stack as second argument to gettable().
//So here Address of table was at top, I pushed
//the index on top, now address is second element
//from top. So I passed it as '-2' in gettable
//below. What gettable() does is that it fetches
//and copies that value at stack top. So I can
//use it from there.
lua_gettable(L,-2);
if(lua_isnumber(L,-1)) //Checking top value replaced by fxn is number...
{
*p++=lua_tointeger(L,-1); //Storing the values in array
}
else
{
lua_pushinteger(L,EXCEPTION_IS_NUMBER);
return 1;
}
lua_pop(L,1);
}
p=q;
sort(p,0,len-1);
for(k=1;k<=len;k++) //This fxn changes the value at prescribed location of table.
//here I am changing the values at Table's location...
//i.e. storing the sorted values in table.....
{
lua_pushinteger(L,k);
lua_pushinteger(L,*p++);
lua_settable(L,-3);
}
lua_pushinteger(L,SUCCESS);
return 1;
}
//Simple quicksort of values.....
void sort(int *arr, int left,int right){
int i = left, j = right;
int tmp;
int pivot = arr[(left + right) / 2];
/* partition */
while (i <= j) {
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j) {
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
};
/* recursion */
if (left < j)
sort(arr, left, j);
if (i < right)
sort(arr, i, right);
}
int luaopen_power(lua_State *L){
lua_register(L,"quicksort",iquicksort);
return 0;
}
I generated a shared library by compiling this program using the following command:-
gcc -Wall -shared -fPIC -o power.so -I/usr/local/include/lua5.1 -llua5.1 quicksort.c
Here is the Lua code for calling this:-
require("power")
x={5,4,6,5,3,2,3,9}
print("Before quicksort call....")
t=quicksort(#x,x)
if t==0 then
for i,v in ipairs(x) do print(i,v) end
else
print(string.format("%s %d","Error occurred. Errorcode is:: ",t))
end
Thanks
The C API for Lua only copies low-level C types, such as numbers and booleans. For all others, including strings, it uses pointers to internal Lua data.
I'm not sure that gettable() copies the value to the Lua stack, I think it copies a reference or pointer of the value... (especially when that value is a table itself, that table 's content is not copied).
And given that Lua may do magic processing, I believe your answer is no.
Lua being free software you can download it and look in the source code. With lua-5.2.0-rc4 the lua_gettable function is in file src/lapi.c
LUA_API void lua_gettable (lua_State *L, int idx) {
StkId t;
lua_lock(L);
t = index2addr(L, idx);
api_checkvalidindex(L, t);
luaV_gettable(L, t, L->top - 1, L->top - 1);
lua_unlock(L);
}
so the actual work is done by luaV_gettable from file src/lvm.c which is
void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {
int loop;
for (loop = 0; loop < MAXTAGLOOP; loop++) {
const TValue *tm;
if (ttistable(t)) { /* `t' is a table? */
Table *h = hvalue(t);
const TValue *res = luaH_get(h, key); /* do a primitive get */
if (!ttisnil(res) || /* result is not nil? */
(tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */
setobj2s(L, val, res);
return;
}
/* else will try the tag method */
}
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))
luaG_typeerror(L, t, "index");
if (ttisfunction(tm)) {
callTM(L, tm, t, key, val, 1);
return;
}
t = tm; /* else repeat with 'tm' */
}
luaG_runerror(L, "loop in gettable");
}
so I think the answer is no. However, you could patch or enhance the code. I don't understand why the question bothers you. Only simple data is copied (very quickly), unless magic occurs (and the magic i.e. metatable is an essential part of Lua semantics); aggregate data content is not copied.
Related
I need to insert into an array which size is unknown an unknown number of elements , the array needs to grow each time i insert an element one by one, until end of data .
how can i do such thing ? can someone give me an example of how to do it ? Thank you.
Arrays in C don't know their size. And inserting a value needs to copy every element from this point to the end of the array (its size). This is impossible in c. But you could implement an array or a list youself.
typedef struct List {
void** data;
int capacity;
int size;
} List;
void init_list(List* l) {
l->data = NULL;
l->capacity = 0;
l->size = 0;
}
void increase_capacity(List* l) {
l->capacity += 8;
l->data = realloc(l->data, sizeof(void*) * l->capacity);
}
void insert(List* l, void* item, int index) {
if (l->capacity <= l->size) {
increase_capacity(l);
}
// or just memmove(l->data + index + 1, l->data + index, l->size - index);
for (int i = list->size; i > index; i--) {
l->data[i] = l->data[i-1];
}
l->data[index] = item;
l->size++;
}
Add other useful functions like add remove and so on.
If you are unsure what functions I use do, read their man pages (for example "man realloc" in the command line or google).
Also, do a C course to know how arrays behave and why other languages know their size and C doesn't.
Spoiler: they just use a struct to remember it.
As part of a personal project, I'm trying to create a dynamic array of 2-tuples that show a) the line in a program and b) the number of bytecode tokens associated with that line. I've implemented this as a struct of arrays:
typedef struct{
int count; // Number of elements
int capacity; // total capacity of arraylist
int* lines;
int* lineCount;
}
this is based on the example from the codebase, as such:
int count;
int capacity;
uint8_t* bytes;
My problem comes from re-allocation - I have several helper functions/macros for growing and re-allocating the array lists memory - here particularly the macro GROW_ARRAY and reallocate(), as described below. When I try and re-allocate lines, it works fine, but I get a segmentation fault and realloc(): invalid old size several times when I attempt to reallocate lineCount after it
I'm using the code base from Bob Nystrom's Crafting Interpreters, especially this first part here https://craftinginterpreters.com/chunks-of-bytecode.html#challenges. Most of the code comes from there, albeit tinkered with some of having added
Mostly, I've added a lot of checks and been running this with all the debug features in gcc I can find. Notably, realloc(): invalid old size has stop appearing as I've tinkered with the code some.
EDIT: Added main function that should reproduce behavior
int main() {
LineArray lines;
// Initialize to 0 / NULL
initLineArray(&lines);
updateLineArray(&lines, 0, 1);
}
// the structure I'm using
typedef struct {
int capacity;
int count;
int* lines;
int* lineCount;
} LineArray;
/* Note LineArray has already been initialized earlier with
capacity=0;
count=0;
lines=NULL;
lineCount=NULL;
*/
void updateLineArray(LineArray* array, int line, int count) {
// IF line in `lines` -- update it
int index = containsLine(array, line);
if (index != -1) { // IF Index is not Error Code
// I think I fixed a bug here?
array->lineCount[index] += count;
return;
}
//ELSE -- add line to end (naturally appends); then increment
else {
//Check to see if array would be overgrown
if (array->capacity < array->count + 1) {
//IF yes, regrow array
int old_capacity = array->capacity;
array->capacity = GROW_CAPACITY(old_capacity);
// Reallocate arrays.
array->lines = GROW_ARRAY(array->lines, int, old_capacity,
array->capacity);
array->lineCount = GROW_ARRAY(array->lineCount, int, old_capacity,
array->capacity);
}
// Properly update the lines
array->lines[array->count] = line;
array->lineCount[array->count] = count;
array->count++;
return;
}
}
//The memory management functions/macros I'm using here
#define GROW_CAPACITY(capacity) \
((capacity) < 8 ? 8 : (capacity) * 2)
#define GROW_ARRAY(previous, type, oldCount, count) \
(type*) reallocate(previous, sizeof(type) * (oldCount), \
sizeof(type) * (count))
void* reallocate(void* previous, size_t oldSize, size_t newSize) {
// If size is null, erase it and get null_pointer
if (newSize == 0) {
free(previous);
return NULL;
}
// reallocate the data into a new size
// is Oldsize is zero :: malloc(data, newSize)
return realloc(previous, newSize);
}
I have implemented code that imports data from a file containing 5 different values, one of them being Time. I have converted the time given in the format Hour.Minute.Second.Millisecond into just Milliseconds.
With this data I created a function Find that finds the data for a given time. This is where the problem arises, since there are multiple days of data here, and the time will repeat multiple times. Is there a function in the C library that returns all instances of a value? Ex.arr =[2,3,4,1,2,] I want it to tell me when the second 2 appears, returning 4.
Edit: For better clarity
These are the functions
void Find(SortedLinkedList *list,int target,int date, char *search) {
if(strcmp(search, "Time") == 0){
Sate *found = findTime(list, target,date);
printf("The Node with time:%d\n Is from the date:%d\n Contains the following:",found->Time,found->Date);
printf("RMag:%6.3f ", found->rmag);
printf("NSmag:%6.3f ", found->NSmag);
printf("azmag:%6.3f ", found->azmag);
printf("avgmag:%6.3f \n", found->avgmag);
}
}
Sate *findTime(SortedLinkedList *list, int target,int date){
Node *current = list->head;
for (int i = 0; i < (list->size)+1 && current != NULL; i++) {
if(current->data->Time == target && current->data->Date == date)
return current->data;
else{
current = current->next;
}
}
}
Right now for it to work I implemented a date insert to differentiate between the times but I'm wondering if it can be done without it.
There's not any kind of a iterate over a collection type of function in the Standard C library other than something like strtok() which will iterate over a text string using the provided token identification pattern.
There is the bsearch() function however that does a search through a sorted list of items and is not really what you want either.
It sounds like you want something like the following. This demonstrates an instantiation of an algorithm however I am not sure what the time points data looks like so that is something you will need to provide.
typedef unsigned long long TimePoint; // made up time data item
typedef struct {
int bFound;
unsigned long ulCurOffset; // array position where item found if bFound is true.
unsigned long ulOffset; // next array position to test
unsigned long ulCount; // count of times found
} IteratorThing;
IteratorThing IterateFunc (IteratorThing x, TimePoint *array, size_t len, TimePoint search)
{
x.bFound = 0; // assume we didn't find one.
// resuming from the current place in the array, search until we
// find a match or we reach the end of the array.
for ( ; x.ulOffset < len; x.ulOffset++) {
// this is a simple comparison for equality which may need to be
// more complex for your specific application.
if (array[x.ulOffset] == search) {
// we have found a match so lets update counts, etc.
x.ulCount++; // count of this search item found.
x.bFound = 1; // indicate we found one.
x.ulCurOffset = x.ulOffset; // remember where we found it.
x.ulOffset++; // point to the next array item to look at
break;
}
}
return x;
}
This would be used as in:
void main_xfun(void)
{
TimePoint array[] = { 1, 2, 3, 2, 3, 4, 0 };
TimePoint search = 2;
size_t len = sizeof(array) / sizeof(array[0]);
{
IteratorThing x = { 0 }; // define and initialize our iterator
while ((x = IterateFunc(x, array, len, search)).bFound) {
// do what is needed when we find a time value
// array offset to the item is x.ulCurOffset
// current count of times found is in x.ulCount;
printf(" found item %d at offset %d count is %d\n", (long)array[x.ulCurOffset], x.ulCurOffset, x.ulCount);
}
printf(" item %d found %d time\n", (long)search, x.ulCount);
}
{
IteratorThing x = { 0 }; // define and initialize our iterator
search = 25;
while ((x = IterateFunc(x, array, len, search)).bFound) {
// do what is needed when we find a time value
// array offset to the item is x.ulCurOffset
// current count of times found is in x.ulCount;
printf(" found item %d at offset %d count is %d\n", (long)array[x.ulCurOffset], x.ulCurOffset, x.ulCount);
}
printf(" item %d found %d time\n", (long)search, x.ulCount);
}
}
produces output of
found item 2 at offset 1 count is 1
found item 2 at offset 3 count is 2
item 2 found 2 time
item 25 found 0 time
To restart the search from the beginning just initialize the iterator struct to all zeros again.
What would be really interesting is to provide a pointer to a comparison function in the interface of the function IterateFunc() which would be called to do the comparisons. This would be along the lines of the bsearch() function which requires a pointer to a comparison function but then that is probably overkill for your specific needs.
If you want this hypothetical function to work for either an array of integers or for your time indexed structures, you will probably need to write a generic function.
If POSIX functions are available to you, you can use lfind() as a starting point for such a generic function.
The lsearch() function shall linearly search the table and return a pointer into the table for the matching entry. If the entry does not occur, it shall be added at the end of the table. ...
The lfind() function shall be equivalent to lsearch(), except that if the entry is not found, it is not added to the table. Instead, a null pointer is returned.
Since lfind() will return the first instance, you need to re-invoke lfind() again past the given instance to find the second instance.
void * lfind_Nth (const void *key, const void *base, size_t *nelp,
size_t width, int (*compar)(const void *, const void *),
int N)
{
const char (*array)[width] = base;
char (*p)[width] = NULL;
size_t n = *nelp;
while (N-- > 0) {
p = n ? lfind(key, array, &n, width, compar) : NULL;
if (p == NULL) break;
n -= (p + 1) - array;
array = p + 1;
}
return p;
}
For your integer array example:
int compar_int (const void *a, const void *b) {
return *(const int *)a != *(const int *)b;
}
int where_Nth_int(int key, int *arr, size_t nelm, int N) {
int *w = lfind_Nth(&key, arr, &nelm, sizeof(*arr),
compar_int, N);
return w ? w - arr : -1;
}
int main (void) {
int arr[] = {2,3,4,1,2,};
int nelm = sizeof(arr)/sizeof(*arr);
printf("Second 2 # %d\n", where_Nth_int(2, arr, nelm, 2));
}
I have an array of typedef structure.
It is declared as such:
vertex vertexArray[numberVertices];
I then have this in my main code:
for(i=0;i<numberVertices;i++)
{
if(vertexArray[i].source == 5)
{
source = vertexArray[i].number;
walk(vertexArray, vertexArray[i], source, headMaxPairList );
}
}
To hopefully perform the walk:
void walk(vertex *vertexArray, vertex v, int source, maxPairing *head)
{
int i;
adjEdge *traverse;
int moveVertex;
int sink;
moveVertex = vertexArray[v.number-1].number;
if(vertexArray[moveVertex-1].color != 5 && vertexArray[moveVertex-1].sink == 5)
{
sink = vertexArray[moveVertex-1].number;
vertexArray[moveVertex-1].color = 5;
addMaxPair(head, source, sink);
}
else
{
walk(vertexArray, vertexArray[moveVertex-1], source, head);
}
}
However, I am getting a seg-fault on the function:
in walk (vertexArray=Cannot access memory at address 0x7fffff3fefe8
I believe this has to do with the way I'm passing vertexArray.
It is my understanding that arrays are actually pointers, hence the vertex *vertexArray and then an individual member is just a vertex, not a pointer hence vertex v.
If anyone could help me with getting this passed correctly I'd be appreciative.
Side note, if anyone can tell if my walk looks like it'll work correctly that's a plus!
Arrays are not same as pointers.
Read the following Links for clarification:
Q: So what is meant by the ``equivalence of pointers and arrays'' in C?
http://c-faq.com/aryptr/aryptrequiv.html
Q: But I heard that char a[] was identical to char *a.
http://c-faq.com/aryptr/aryptr2.html
To avoid Seg-Fault, add the following checks to walk() function:
void walk(vertex *vertexArray, vertex v, int source, maxPairing *head)
{
int i;
adjEdge *traverse;
int moveVertex;
int sink;
/* Add this Check to Avoid Seg Fault, you need to make the value of
'numberVertices'available to this function as this is your array size */
if ((((v.number-1)<0)||((v.number-1)>numberVertices))
{
return;
}
/* Check Ends */
moveVertex = vertexArray[v.number-1].number;
/* Another Check */
if((moveVertex-1<0)||(moveVertex-1>numberVertices))
{
return;
}
/* Check Ends */
if(vertexArray[moveVertex-1].color != 5 && vertexArray[moveVertex-1].sink == 5)
{
sink = vertexArray[moveVertex-1].number;
vertexArray[moveVertex-1].color = 5;
addMaxPair(head, source, sink);
}
else
{
walk(vertexArray, vertexArray[moveVertex-1], source, head);
}
I'm assuming that the good old qsort function in stdlib is not stable, because the man page doesn't say anything about it. This is the function I'm talking about:
#include <stdlib.h>
void qsort(void *base, size_t nmemb, size_t size,
int(*compar)(const void *, const void *));
I assume that if I change my comparison function to also include the address of that which I'm comparing, it will be stable. Is that correct?
Eg:
int compareFoos( const void* pA, const void *pB ) {
Foo *pFooA = (Foo*) pA;
Foo *pFooB = (Foo*) pB;
if( pFooA->id < pFooB->id ) {
return -1;
} else if( pFooA->id > pFooB->id ) {
return 1;
} else if( pA < pB ) {
return -1;
} else if( pB > pA ) {
return 1;
} else {
return 0;
}
}
No, you cannot rely on that unfortunately. Let's assume you have the array (two fields in each record used for checking but only first field used for sorting):
B,1
B,2
A,3
A non-stable sort may compare B,1 with A,3 and swap them, giving:
A,3
B,2
B,1
If the next step were to compare B,2 with B,1, the keys would be the same and, since B,2 has an address less than B,1, no swap will take place. For a stable sort, you should have ended up with:
A,3
B,1
B,2
The only way to do it would be to attach the starting address of the pointer (not its current address) and sort using that as well as the other keys. That way, the original address becomes the minor part of the sort key so that B,1 will eventually end up before B,2 regardless of where the two B lines go during the sorting process.
The canonical solution is to make (i.e. allocate memory for and fill) an array of pointers to the elements of the original array, and qsort this new array, using an extra level of indirection and falling back to comparing pointer values when the things they point to are equal. This approach has the potential side benefit that you don't modify the original array at all - but if you want the original array to be sorted in the end, you'll have to permute it to match the order in the array of pointers after qsort returns.
This does not work because during the sort procedure, the ordering will change and two elements will not have consistent output. What I do to make good old-fashioned qsort stable is to add the initial index inside my struct and initialize that value before passing it to qsort.
typedef struct __bundle {
data_t some_data;
int sort_score;
size_t init_idx;
} bundle_t;
/*
.
.
.
.
*/
int bundle_cmp(void *ptr1, void *ptr2) {
bundle_t *b1, *b2;
b1 = (budnel_t *) ptr1;
b2 = (budnel_t *) ptr2;
if (b1->sort_score < b2->sort_score) {
return -1;
}
if (b1->sort_score > b2->sort_score) {
return 1;
}
if (b1->init_idx < b2->init_idx) {
return -1;
}
if (b1->init_idx > b2->init_idx) {
return 1;
}
return 0;
}
void sort_bundle_arr(bundle_t *b, size_t sz) {
size_t i;
for (i = 0; i < sz; i++) {
b[i]->init_idx = i;
}
qsort(b, sz, sizeof(bundle_t), bundle_cmp);
}