b_a_t: (Default)
[personal profile] 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(хотя до сих пор работало:)).
Page 1 of 3 << [1] [2] [3] >>

Date: 2005-03-02 08:53 pm (UTC)
From: [identity profile] msh.livejournal.com
Тех, кто приводит между собой поинтеры и int-ы мы будем ссылать на БАМ с книжкой про 64-битные процессоры и большой лопатой



Date: 2005-03-02 08:55 pm (UTC)
From: [identity profile] b-a-t.livejournal.com
Ну я ж вроде не привожу :)?

Вообще я эту конструкцию на автомате писал, так что уже и не уверен :)

Date: 2005-03-02 08:58 pm (UTC)
From: [identity profile] msh.livejournal.com
А чего тогда про размер указателя спрашиваешь? ;-)

Date: 2005-03-02 09:03 pm (UTC)
From: [identity profile] b-a-t.livejournal.com
Так в Perl'e указателей-то нет :)

А вот например в glib есть функция pointer2int(). Или макрос. И обратный(-ая). Вот кто они после этого :)?
From: [identity profile] poige.livejournal.com
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
Комфорт + меньшая вероятность ошибки. ;-)
From: [identity profile] b-a-t.livejournal.com
Ну, тоже вариант :) Спасибо :)

Пожалуйста

Date: 2005-03-02 09:11 pm (UTC)
From: [identity profile] poige.livejournal.com
Там, правда, во второй строке EXTATTR_NAMESPACE_SYSTEM неправильно указан, должно быть _USER. ;-) Но смысл, конечно, не в этом.

Date: 2005-03-02 09:31 pm (UTC)
From: [identity profile] uncle-asa.livejournal.com
Для тех, кому надо в одном и том же месте хранить разную фигню, придумали 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);
}

Date: 2005-03-02 09:34 pm (UTC)
From: [identity profile] uncle-asa.livejournal.com
И type ещё enum'ом по-хорошему надо бы сделать.
From: [identity profile] ex-croco667.livejournal.com
А вот имя макроса я бы написал uppercase. В смысле, не extattr_STRnLEN, а EXTATTR_STRNLEN. Традиция такая. "Осторожно, злая собака макрос".

Date: 2005-03-03 01:13 am (UTC)
From: [identity profile] ex-croco667.livejournal.com
Самое-то интересное, что он вовсе не поинтер к инту приводит, а всего лишь сначала из int* делает const void*, а потом из этого const void* делает обратно int* и тут же его равыменовывает. Что в-общем ничего страшного, ибо imho архитектуры, на который размер указателя зависит от типа указуемой переменной, суть из области Страшных Сказок Для Неофитов (хотя стандарт предполагает и это).

Только вот текст постинга (не код) наводит на крамольную мысль, что сам автор кода не ведает, что творит :))))

Date: 2005-03-03 01:56 am (UTC)
From: [identity profile] duke-igthorn.livejournal.com
А можно я немножко побуквоедствую?:)

1. Хорошо бы функции extattr_list_* свести в массивчик (да, а первый параметр сделать именно union). Тогда вместо дурацкого switch был бы вызов функции из массива. Причем указатель в этом массиве, очевидно, вычисляется снаружи главного цикла. Вообще, у тебя хроническая болезнь - невынос инвариантов из циклов (да да, я в курсе, что-то может соптимизировать компилятор - но лучше не полагаться на волю случая...:)
2. Циклы от 0 до n менее эффективны, чем от n до 0. Потому как операция сравнения с 0 - дешевле. Типа for (i=n;--i>=0;)
3. Немного глупо каждый раз делать extattr[t] - надо просто указателем гулять по extattr.

Date: 2005-03-03 02:01 am (UTC)
From: [identity profile] uncle-asa.livejournal.com
Ещё тут можно отыграть чуть-чуть:
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;
}
From: [identity profile] poige.livejournal.com
... макрос "очень локальный". Так что читаемость и легкость идентификации (в качестве префикса в имени макроса выбрано имя структуры as is) для меня на первом месте.

Date: 2005-03-03 04:03 am (UTC)
From: [identity profile] b-a-t.livejournal.com
Что, и такое возможно?! Ужас, а я и не знал/не помнил... Кошмар этот ваш С :)

Я пытаюсь сэмулировать линуксячие функции на FreeBSD :) Может, в Линук эмулятор это дело заодно продать?

Date: 2005-03-03 05:52 am (UTC)
From: [identity profile] b-a-t.livejournal.com
О, кстати да! Про 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) :)))

Date: 2005-03-03 05:52 am (UTC)
From: [identity profile] b-a-t.livejournal.com
Ну, это да, или #define хотя бы... Но кусок шибко локальный, так что это уже overkill мне кажется...

Date: 2005-03-03 06:19 am (UTC)
From: [identity profile] b-a-t.livejournal.com
Сдается мне, ты слишком долго с Явой работал :)Вводить 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 засорять :)

Date: 2005-03-03 07:46 am (UTC)
From: [identity profile] duke-igthorn.livejournal.com
Я, как ты помнишь, и на сях иногда балуюсь, thanks to GNOME:)

1. wrapper можно заинлайнить (он же однострочный и простой). Это все compile-time время. Зато гарантировано константное (и небольшое - в случае инлайна) время вызова конечной функции - не if-then-elif-then-else... (еще и умноженное на кол-во проходов цикла!) Да и вообще архитектурно элегантно-выпендрежнее получается:)
2. Ты меня убил асмовским кодом... Эти уроды, чтобы с 0 сравнить - в стек его грузят? Тогда да, беру свои слова обратно про оптимальность цикла. И начинаю ругаться на gcc...:) Пойду покомпилирую что ли, поизучаю... Еще в древние времена всякие bcc/cl умели нормально с 0 на x86 сравнивать...
3. А указатель таки введи, для красоты-то:)

Date: 2005-03-03 07:54 am (UTC)
From: [identity profile] duke-igthorn.livejournal.com
2. Тьфу, я тормоз. Сравнивают они с абсолютным значением. Тут все намана. Но! Если компилить хотя бы -O2 получается, например, такое
3d: 4b dec %ebx
3e: 79 ec jns 2c <main+0x2c>
Вот так при limit != 0 - не с оптимизировать...

Date: 2005-03-03 08:35 am (UTC)
From: [identity profile] b-a-t.livejournal.com
switch не есть if-elsif-eslif-else. Посмотри во что он разворачивается :) Во многом, он та же таблица с точками входа, только еще более плоская, чем доступ через указатели. Вот в Перле тамлицы с указателями на функции очень красиво смотрятся, так как там нет сигнатур функций, можно всего намешать. И вообще, в конечном итоге это получается таблица виртуальных ф-ций :)

Date: 2005-03-03 10:07 am (UTC)
From: [identity profile] b-a-t.livejournal.com
Покажи, что ты оптимизируешь там :)?

У меня с оптимизатором код совершенно одинаковый для:

for(i=10; i >= 0; i--) { }

и

for(i=0; i < 10; i++) { }

Date: 2005-03-03 10:17 am (UTC)
From: [identity profile] b-a-t.livejournal.com
Ага, хорошая мысль! Почти так и переписал:

{ 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;
}

Date: 2005-03-03 10:40 am (UTC)
From: [identity profile] duke-igthorn.livejournal.com
Двоечник! Твой цикл с -- выполняется 11 раз!:)
Я делаю
for (int i=10;--i>=0;) {}

Date: 2005-03-03 10:41 am (UTC)
From: [identity profile] duke-igthorn.livejournal.com
Дык я про это и говорю - реально предлагаю тебе создать массив функций - и тогда на сигнатуры наплевать (и не выяснять в каждом проходе цикла "а какой у нас сегодня type")
Page 1 of 3 << [1] [2] [3] >>

June 2025

S M T W T F S
123 4567
891011121314
15161718192021
22232425262728
2930     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Feb. 25th, 2026 12:12 pm
Powered by Dreamwidth Studios