?

Log in

No account? Create an account
Журнал Друзья Календарь Биография Пещера Назад Назад Вперед Вперед
Программистское. Скучное. - Timur I. Bakeyev
Безумный Тимурк
b_a_t
b_a_t
Программистское. Скучное.
После 5 лет тотальной Perl лоботомии я понял, что забыл С практически начисто... Провозился 2 дня с вылизыванием 80 строчек кода :( Но до сих пор не уверен, что сделал все правильно. Так что, если кто присоветует, что тут еще можно улучщить - пишите :) Да, если по скорости какие-то оптимизации - тоже :)

Да, код под FreeBSD5 :)
#include <errno.h>
#include <sys/types.h>
#include <sys/extattr.h>
#include <stdlib.h>
#include <string.h>

#include <stdio.h>
#include <fcntl.h>

static struct {
    int space;
    const char *name;
    size_t len;
} 
extattr[] = {
	{ EXTATTR_NAMESPACE_SYSTEM, “system.”, sizeof(“system.”) },

        { EXTATTR_NAMESPACE_USER, “user.”, sizeof(“user.”) },
};

ssize_t my_listxattr (int type, const void *arg, char *list, size_t size) {
	ssize_t list_size, tot_size = 0;
	int i, t, len;
	char *buf;
	/* Iterate through extattr(2) namespaces */
	for(t = 0; t < sizeof(extattr)/sizeof(extattr[0]); t++) {
		switch(type) {
			case 0:
				list_size = extattr_list_file((const char*)arg, extattr[t].space, list, size);
				break;
			case 1:
				list_size = extattr_list_link((const char*)arg, extattr[t].space, list, size);
				break;
			case 2:
				list_size = extattr_list_fd(*(int*)arg, extattr[t].space, list, size);
				break;
			default:
				errno = ENOSYS;
				return -1;
		}
		/* Some error happened. Errno should be set by previous call */
		if(list_size < 0)
			return -1;
		/* No attributes */
		if(list_size == 0)
			continue;
		/* XXX: Call with an empty buffer may be used to calculate
		   necessary buffer size. Unfortunately, we can't say, how
		   many attributes were returned, so here is the potential
		   problem with the emulation.
		*/
		if(list == NULL) {
			/* For sure, at least one attribute exists. */
			tot_size += list_size + extattr[t].len - 1;
			continue;
		}
		/* Count necessary offset to fit namespace prefixes */
		len = 0;
		for(i = 0; i < list_size; i += list[i] + 1)
			len += extattr[t].len - 1;

		tot_size += list_size + len;
		/* Buffer is too small to fit the results */
		if(tot_size > size) {
			errno = ERANGE;
			return -1;
		}
		/* Shift the results back, so we can prepend prefixes */
		buf = memmove(list + len, list, list_size);

		for(i = 0; i < list_size; i += len + 1) {
			len = buf[i];
			strncpy(list, extattr[t].name, extattr[t].len);
			strncat(list, buf + i + 1, len);
			list += extattr[t].len + len;
		}
		size -= tot_size;
	}
	return tot_size;
}

ssize_t sys_listxattr (const char *path, char *list, size_t size)
{
	return my_listxattr(0, path, list, size);
}

ssize_t sys_llistxattr (const char *path, char *list, size_t size)
{
	return my_listxattr(1, path, list, size);
}

ssize_t sys_flistxattr (int filedes, char *list, size_t size)
{
	return my_listxattr(2, &filedes, list, size);
}

int main(int argc, char *argv[]) {
    char buf[1024];
    ssize_t size;
    int fd, i = 0;
    
    //size = sys_listxattr(argv[1], buf, sizeof(buf));
    
    fd = open(argv[1], O_RDONLY);
    
    size = sys_flistxattr(fd, buf, sizeof(buf));
    //size = sys_flistxattr(fd, NULL, 0);
    
    printf("Len: %d\n", size);
    for(i = 0; i < size; i += strlen(buf+i)+1) {
	printf("%s\n", buf+i);
    }
    return 0;
}


Кстати, не уверен, насколько верно передавать int a в функцию с void* ptr через &a, а потом приводить через *(int*)ptr. Но в теории, размера указателя может не хватить, чтобы поместить в него int(хотя до сих пор работало:)).

Current Mood: stupid

Читать 59 комментария или Leave a comment
Comments
msh From: msh Date: March 2nd, 2005 08:53 pm (UTC) (Link)
Тех, кто приводит между собой поинтеры и int-ы мы будем ссылать на БАМ с книжкой про 64-битные процессоры и большой лопатой



b_a_t From: b_a_t Date: March 2nd, 2005 08:55 pm (UTC) (Link)
Ну я ж вроде не привожу :)?

Вообще я эту конструкцию на автомате писал, так что уже и не уверен :)
poige From: poige Date: March 2nd, 2005 09:05 pm (UTC) (Link)

Я возьму вот этот кусочек, для начала:

extattr[] = {
	{ EXTATTR_NAMESPACE_SYSTEM, “system.”, sizeof(“system.”) },
        { EXTATTR_NAMESPACE_USER, “user.”, sizeof(“user.”) },
};
я бы сделал так:
#define extattr_STRnLEN(str) str, sizeof(str)
extattr[] = {
    { EXTATTR_NAMESPACE_SYSTEM, extattr_STRnLEN("system.") },
    { EXTATTR_NAMESPACE_SYSTEM, extattr_STRnLEN("user.") }
};
#undef extattr_STRnLEN
Комфорт + меньшая вероятность ошибки. ;-)
b_a_t From: b_a_t Date: March 2nd, 2005 09:09 pm (UTC) (Link)

Re: Я возьму вот этот кусочек, для начала:

Ну, тоже вариант :) Спасибо :)
From: ex_croco667 Date: March 3rd, 2005 01:07 am (UTC) (Link)

Re: Я возьму вот этот кусочек, для начала:

А вот имя макроса я бы написал uppercase. В смысле, не extattr_STRnLEN, а EXTATTR_STRNLEN. Традиция такая. "Осторожно, злая собака макрос".
uncle_asa From: uncle_asa Date: March 2nd, 2005 09:31 pm (UTC) (Link)
Для тех, кому надо в одном и том же месте хранить разную фигню, придумали union.
typedef union extattr_arg
{
const char *file;
const char *link;
int fd;
} extattr_arg;

ssize_t my_listxattr (int type, extattr_arg arg, char *list, size_t size) {
...
case 0:
list_size = extattr_list_file(arg.file, extattr[t].space, list, size);
break;
case 1:
list_size = extattr_list_link(arg.link, extattr[t].space, list, size);
break;
case 2:
list_size = extattr_list_fd(arg.fd, extattr[t].space, list, size);
break;
...
ssize_t sys_listxattr (const char *path, char *list, size_t size)
{
extattr_arg arg;
arg.file = path;
return my_listxattr(0, arg, list, size);
}

ssize_t sys_llistxattr (const char *path, char *list, size_t size)
{
extattr_arg arg;
arg.link = path;
return my_listxattr(1, arg, list, size);
}

ssize_t sys_flistxattr (int filedes, char *list, size_t size)
{
extattr_arg arg;
arg.fd = filedes;
return my_listxattr(2, arg, list, size);
}
b_a_t From: b_a_t Date: March 3rd, 2005 05:52 am (UTC) (Link)
О, кстати да! Про union я как-то совсем-совсем забыл :) Надо будет попробовать :)

Но вот представь как аналогичная функциия для IRIX написана:

static ssize_t irix_attr_list(const char *path, int filedes, char *list, size_t size, int flags);

Вот те path и filedes - это то что я через void* передаю :) А там - либо (1, 0), либо (0, 1) :)))
uncle_asa From: uncle_asa Date: March 2nd, 2005 09:34 pm (UTC) (Link)
И type ещё enum'ом по-хорошему надо бы сделать.
b_a_t From: b_a_t Date: March 3rd, 2005 05:52 am (UTC) (Link)
Ну, это да, или #define хотя бы... Но кусок шибко локальный, так что это уже overkill мне кажется...
duke_igthorn From: duke_igthorn Date: March 3rd, 2005 01:56 am (UTC) (Link)
А можно я немножко побуквоедствую?:)

1. Хорошо бы функции extattr_list_* свести в массивчик (да, а первый параметр сделать именно union). Тогда вместо дурацкого switch был бы вызов функции из массива. Причем указатель в этом массиве, очевидно, вычисляется снаружи главного цикла. Вообще, у тебя хроническая болезнь - невынос инвариантов из циклов (да да, я в курсе, что-то может соптимизировать компилятор - но лучше не полагаться на волю случая...:)
2. Циклы от 0 до n менее эффективны, чем от n до 0. Потому как операция сравнения с 0 - дешевле. Типа for (i=n;--i>=0;)
3. Немного глупо каждый раз делать extattr[t] - надо просто указателем гулять по extattr.
b_a_t From: b_a_t Date: March 3rd, 2005 06:19 am (UTC) (Link)
Сдается мне, ты слишком долго с Явой работал :)Вводить wrapper для того, чтобы избежать switch - это очень хитрая оптимизация :> Да и накладные расходы по хождению по массиву и вызову ф-ции по указателю... В общем, не думаю что это сбережет циклы CPU :)

А на чем основана мысль, что с 0 сравнивать дешевле?

@@ -10,16 +10,16 @@
movl %esp,%ebp
subl $24,%esp
nop
- movl $10,-4(%ebp)
+ movl $0,-4(%ebp)
.p2align 4,,7
.L3:
- cmpl $0,-4(%ebp)
- jg .L5
+ cmpl $9,-4(%ebp)
+ jle .L5
jmp .L4
.p2align 4,,7
.L6:
.L5:
- decl -4(%ebp)
+ incl -4(%ebp)
jmp .L3
.p2align 4,,7
.L4:


Результат сравнения прямого и обратного цикла, скомпилированного в ассемблер :) Как видишь - одинаковый абсолютно код :)

А вот насчет указателя верно замечено :) Правда, опять же - придется тип вводить, чтобы такой указатель создать... Не хотелось бы namespace засорять :)
uncle_asa From: uncle_asa Date: March 3rd, 2005 02:01 am (UTC) (Link)
Ещё тут можно отыграть чуть-чуть:
for(i = 0; i < list_size; i += len + 1) {
len = buf[i];
strncpy(list, extattr[t].name, extattr[t].len);
strncat(list, buf + i + 1, len);
list += extattr[t].len + len;
}
переделав в:
for(i = 0; i < list_size; i += len + 1) {
len = buf[i];
// меньше на проверку внутри, а строка у нас точно NUL-терминирована в известном месте
strcpy(list, extattr[t].name);
// не ищем конец строки перед добавлением в конец, потому как знаем где он
list += extattr[t].len;
// если в bsd есть stxcpy, то две следующие строчки меняются на него
strncpy(list, buf + i + 1, len-1); // а можно и memcpy в общем-то - будет ещё быстрее
list[len-1] = '\0';
list += len;
}
b_a_t From: b_a_t Date: March 3rd, 2005 10:17 am (UTC) (Link)
Ага, хорошая мысль! Почти так и переписал:

{ EXTATTR_NAMESPACE_SYSTEM, "system.", sizeof("system.")-1 },

...

for(i = 0; i < list_size; i += len + 1) {
len = buf[i];
strcpy(list, extattr[t].name);
list += extattr[t].len;
strncpy(list, buf + i + 1, len);
list[len] = '\0';
list += len + 1;
}
poige From: poige Date: March 4th, 2005 06:02 am (UTC) (Link)

Ну вот, ты получил столько "feed-back"'а...

И где патч, я спрашиваю? ;-)
b_a_t From: b_a_t Date: March 4th, 2005 06:04 am (UTC) (Link)

Re: Ну вот, ты получил столько "feed-back"'а...

cd /usr/ports/net/samba3/files/; cat patch-lib_system.c :)))

Но это видимо со следующим релизом.
Читать 59 комментария или Leave a comment