Given an array of any type (integers in this case) and a Map that tells what indexes are supposed to be swapped in an array. I am attempting to make a clean swap, but running into issues with the way I am using memcpy.
Here is what I have so far:
Goal: given a data array of [1,3,-1,2] and a mapping of [[0,3],[3,2],[2,1],[1,0]], a clean permutation would be [3,-1,2,1].
My current implementation : 0 3 -1 2… I think I have an off-by-one error somewhere.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define MAP_SIZE 4
typedef struct MapEntry {
int indexFrom;
int indexTo;
} MapEntry;
typedef MapEntry * Map;
int permute(void *data, int nblobs, int szblob, const Map map);
void build_map(Map);
void build_data(int *);
int is_map_valid(Map);
void print_map(Map);
int is_valid(Map);
int map_comparator(const void * a, const void * b);
int main(int argc, char const *argv[])
{
int nblobs, * data, i;
size_t szblob;
Map map = (Map)malloc(sizeof(Map));
data = (int *) malloc(sizeof(int) * 4);
build_map(map);
data[0] = 1;
data[1] = 3;
data[2] = -1;
data[3] = 2;
nblobs = 4;
szblob = sizeof(int);
if (!permute(data, nblobs, szblob, map)) {
printf("Invalid Map\n");
return 0;
}
i = 0;
for (i = 0; i < szblob; ++i) {
printf("%d ", data[i]);
}
return 0;
}
void print_map(Map map){
int i;
for (i = 0; i < MAP_SIZE; ++i) {
printf("[%d - %d]\n", map[i].indexFrom, map[i].indexTo);
}
}
int map_comparator(const void *a, const void *b)
{
const MapEntry *s1 = a;
const MapEntry *s2 = b;
if (s2->indexFrom != s1->indexFrom) {
return s1->indexFrom - s2->indexFrom;
} else {
return s1->indexTo - s2->indexTo;
}
}
int is_map_valid(Map map) {
int i,j;
for (i = 1; i < MAP_SIZE; ++i){
j = i - 1;
if (map[j].indexFrom == map[i].indexFrom)
return 0;
if (map[j].indexTo == map[i].indexTo)
return 0;
}
return 1;
}
int is_valid(Map map) {
qsort(map, MAP_SIZE, sizeof(MapEntry), map_comparator);
if (!is_map_valid(map)) return 0;
return 1;
}
int permute(void *data, int nblobs, int szblob, const Map map){
int i, tmpFrom, tmpTo;
void * a = (void *)malloc(szblob);
char *p = data;
/* check if map has duplicate keys */
/* sort the list, then check whether or not the map is valid */
if (!is_valid(map)) return 0;
/* where issues occur */
for (i = 0; i < nblobs; ++i){
tmpFrom = map[i].indexFrom;
tmpTo = map[i].indexTo;
memcpy(a, &p[tmpFrom*szblob], szblob);
memcpy(&p[tmpFrom*szblob], &p[tmpTo*szblob], szblob);
memcpy(&p[tmpTo*szblob], a, szblob);
}
return 1;
}
/* build mapping */
void build_map(Map map){
map[0].indexFrom = 0;
map[0].indexTo = 3;
map[1].indexFrom = 3;
map[1].indexTo = 2;
map[2].indexFrom = 2;
map[2].indexTo = 1;
map[3].indexFrom = 1;
map[3].indexTo = 0;
}
You are a victim of the non-standard GCC extension that is enabled by default and allows pointer arithmetics on pointers to void and pointers to function by treating the size of a void or of a function as 1. This extension can be disabled by specifying a C standard as, say, C99, using
-stdoption — for example,-std=c99(see gcc manual page for details). Alternatively, you may ask gcc to issue a warning for such cases by specifying-Wpointer-arithoption.Back to the problem, consider what happens when you write
&data[tmpFrom]. An address pointed bydatais taken, thentmpFrombytes are added to that address. What you want instead is to addtmpFrom * sizeof(int)bytes. To achieve that, you have to either manually calculate required number of bytes based on the value oftmpFromand the size ofinttype, or declaredatapointer as a pointer to typeint. The second is a preferred way to go, but if you really want your functions to support arbitrary data types, then you have to fall back to a harder, first approach.Below is a list of warnings generated by clang (it is generally a lot better with diagnostics):
Once the above warnings are fixed, it should have worked. However, there are two more problems…
The first problem is incorrect expected result. It should be
3, -1, 1, 2and not3, -1, 2, 1. The mapping should be sorted like this:And the permutation should be done in four steps:
The second problem is incorrect sorting. By performing two sorts, first on “from” values and second on “to” values, you end up with a mapping sorted only by “to” (the last sort you invoke). What should be done instead is a single sort using a predicate that compares both “from” and “to” of each element. For example:
Once the above is fixed, everything will work. Other than that, there are only few more suggestions to your code that might be helpful:
indexFromandindexTofields ofMapEntrystructure dynamically allocated.void *. For example:void * a = (void *)malloc(szblob);should be justvoid *a = malloc(szblob);.void *to other pointer types likeint *. This is not necessary in C wherevoid *pointer is implicitly convertible to pointers of other types. This is not true for C++, however.typedefstructures unless the goal is to create an opaque type (which is not in your case). Typingstructmay seem like a lot of typing, but it serves as a great hints about the type to those C developers who read your code. For example, see Chapter 5 of the Linux Kernel Coding Style for a great explanation.I encourage you to fix your code yourself, but here is your code with minimal necessary changes to make it work, for your reference:
Hope it helps. Stay warm and Good Luck!