Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Показано с 1 по 7 из 7
  1. #1
    Аватар для punkochel
    Пользователь

    Статус
    Оффлайн
    Регистрация
    08.12.2018
    Адрес
    Россия
    Сообщений
    146
    Репутация:
    25 ±

    Конвертация IP-адреса

    Описание:
    Функции преобразования IP-адреса в целочисленное значение и обратно в строку. Сравнение целых чисел происходит быстрее чем сравнение строк.

    Функции:
    1. stock IPv4ToInteger(const ip_address[])
    2. { // by punkochel (pro-pawn.ru)
    3. new count,
    4. part[4],
    5. str_value[3+1];
    6.  
    7. for(new i, k; ip_address[i]; i++) {
    8. if(ip_address[i] == '.') {
    9. part[count++] = strval(str_value);
    10. k = 0;
    11. continue;
    12. }
    13. str_value[k++] = ip_address[i];
    14. str_value[k] = '\0';
    15. }
    16. part[count] = strval(str_value);
    17.  
    18. return part[3] | (part[2] << 8) | (part[1] << 16) | (part[0] << 24);
    19. }
    20.  
    21. stock IntegerToIPv4(ip_address, result[])
    22. { // by punkochel (pro-pawn.ru)
    23. new part[4];
    24. for(new i; i < 4; i++)
    25. part[i] = (ip_address >> i*8) & 255;
    26.  
    27. format(result, 15+1, "%d.%d.%d.%d", part[3], part[2], part[1], part[0]);
    28. return 1;
    29. }


    Для тех кто использует плагин sscanf:
    1. stock IPv4ToInteger(const ip_address[])
    2. { // by punkochel (pro-pawn.ru)
    3. new a, b, c, d;
    4. sscanf(ip_address, !"p<.>iiii", a, b, c, d);
    5. return d | (c << 8) | (b << 16) | (a << 24);
    6. }


    Автор: punkochel

    *Исключительно для pro-pawn.ru
    Копирование данной статьи на других ресурсах без разрешения автора или Daniel_Cortez запрещено!
    Последний раз редактировалось punkochel; 05.04.2023 в 00:15. Причина: изменил порядок расположения байтов

  2. Пользователь сказал cпасибо:
    execution (28.09.2023)
  3. #2
    Аватар для Shaolinka
    Пользователь

    Статус
    Оффлайн
    Регистрация
    19.01.2020
    Сообщений
    69
    Репутация:
    8 ±
    Стоило бы внести проверку на то, не является ли одно из чисел IP выше 8 бит(> 255), дабы не нарушался алгоритм. Насчёт конвертации из целого в строку с айпи адресом, то там имеется проблема в том, что цикл стартует от нуля и вплоть до трёх. Потому сдвигаться вправо на 8 бит будут биты из диапазона: 0 * 8 - 8; 1 * 8 = 8; 2 * 8 = 16. Но никак не 8 <= 24; Потому таков цикл должен стартовать от единицы и до трёх включительно. По-поводу версии с sscanf'om, то можешь воспользоваться массивом, проитерировавшись по нему вскоре, присваивая результат возвращаемой переменной исходя из формулы: массив, куда сохранились целые числа айпишника[i - 1] << i * 8; Таков цикл обязан стартовать, как обычно от единицы до трёх включительно. А так-то молодец, полезно, в общем доступе не замечал такого, в основном приходилось импровизировать собственноручно. Глядишь, сподвигнешь кого не выделять 64 байта(16 * 4) впустую, отдавая должное обыкновенной переменной в 4 байта

  4. #3
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±
    Лучше использовать возвращаемое значение для ошибок, а сам результат возвращать через ссылку. Кроме того, можно использовать упакованный массив для октетов, а уже после возвращать целую ячейку. Значение лучше конвертировать без функции strval - это намного дешевле вызова функции.

    1. stock IPv4ToInteger(&dst, const src[]) {
    2. new i = 0, chr, buf[3 char], j = 0, val = 0;
    3. while ((chr = src[i++]) != EOS) {
    4. switch (chr) {
    5. case '0'..'9': {
    6. val = val * 10 + chr - '0';
    7. if (val > 255) {
    8. return -1;
    9. }
    10. }
    11. case '.': {
    12. if (j > 3) {
    13. return -1;
    14. }
    15. buf{j++} = val;
    16. val = 0;
    17. }
    18. default: {
    19. return -1;
    20. }
    21. }
    22. }
    23. dst = buf[0] | val;
    24. return 0;
    25. }

  5. 2 пользователя(ей) сказали cпасибо:
    execution (28.09.2023) punkochel (05.04.2023)
  6. #4
    Аватар для punkochel
    Пользователь

    Статус
    Оффлайн
    Регистрация
    08.12.2018
    Адрес
    Россия
    Сообщений
    146
    Репутация:
    25 ±
    Цитата Сообщение от Shaolinka Посмотреть сообщение
      Открыть/закрыть
    Стоило бы внести проверку на то, не является ли одно из чисел IP выше 8 бит(> 255), дабы не нарушался алгоритм. Насчёт конвертации из целого в строку с айпи адресом, то там имеется проблема в том, что цикл стартует от нуля и вплоть до трёх. Потому сдвигаться вправо на 8 бит будут биты из диапазона: 0 * 8 - 8; 1 * 8 = 8; 2 * 8 = 16. Но никак не 8 <= 24; Потому таков цикл должен стартовать от единицы и до трёх включительно. По-поводу версии с sscanf'om, то можешь воспользоваться массивом, проитерировавшись по нему вскоре, присваивая результат возвращаемой переменной исходя из формулы: массив, куда сохранились целые числа айпишника[i - 1] << i * 8; Таков цикл обязан стартовать, как обычно от единицы до трёх включительно. А так-то молодец, полезно, в общем доступе не замечал такого, в основном приходилось импровизировать собственноручно. Глядишь, сподвигнешь кого не выделять 64 байта(16 * 4) впустую, отдавая должное обыкновенной переменной в 4 байта
    Честно говоря, я многое не понял что ты имеешь ввиду)
    Валидность IP это прерогатива вызывающего, если ему это нужно, то он может конечно использовать и регулярные выражения. В общем оставим это на совести скриптера.
    По поводу массива в функции со sscanf, то тут выбор сделан в пользу скорости, ибо читабельность сохраняется.
    Все что касается сдвигов, то тут никаких проблем нет.
    Благодарю за здоровую критику.

    - - - Добавлено - - -

    Цитата Сообщение от VVWVV Посмотреть сообщение
      Открыть/закрыть
    Лучше использовать возвращаемое значение для ошибок, а сам результат возвращать через ссылку. Кроме того, можно использовать упакованный массив для октетов, а уже после возвращать целую ячейку. Значение лучше конвертировать без функции strval - это намного дешевле вызова функции.

    1. stock IPv4ToInteger(&dst, const src[]) {
    2. new i = 0, chr, buf[3 char], j = 0, val = 0;
    3. while ((chr = src[i++]) != EOS) {
    4. switch (chr) {
    5. case '0'..'9': {
    6. val = val * 10 + chr - '0';
    7. if (val > 255) {
    8. return -1;
    9. }
    10. }
    11. case '.': {
    12. if (j > 3) {
    13. return -1;
    14. }
    15. buf{j++} = val;
    16. val = 0;
    17. }
    18. default: {
    19. return -1;
    20. }
    21. }
    22. }
    23. dst = buf[0] | val;
    24. return 0;
    25. }
    Да, пожалуй ты прав, что strval здесь не лучшее решение.
    Что касается возврата ошибок, то я не думаю что стоит каждый раз проверять валидность IP. Лучше это делать в другом месте. Функция лишь выполняет то зачем и вызывается.
    Последний раз редактировалось punkochel; 05.04.2023 в 19:30.

  7. #5
    Аватар для Shaolinka
    Пользователь

    Статус
    Оффлайн
    Регистрация
    19.01.2020
    Сообщений
    69
    Репутация:
    8 ±
    Цитата Сообщение от punkochel Посмотреть сообщение
    Честно говоря, я многое не понял что ты имеешь ввиду)
    Валидность IP это прерогатива вызывающего, если ему это нужно, то он может конечно использовать и регулярные выражения. В общем оставим это на совести скриптера.
    По поводу массива в функции со sscanf, то тут выбор сделан в пользу скорости, ибо читабельность сохраняется.
    Все что касается сдвигов, то тут никаких проблем нет.
    Благодарю за здоровую критику.

    - - - Добавлено - - -



    Да, пожалуй ты прав, что strval здесь не лучшее решение.
    Что касается возврата ошибок, то я не думаю что стоит каждый раз проверять валидность IP. Лучше это делать в другом месте. Функция лишь выполняет то зачем и вызывается.
    Разница не столь существенна в том, чтобы обратиться к индексу массива, а не к физическому адресу переменной. В прочем да, если значений немного, то можно обойтись и переменными. Дело вкуса вообщем. Насчёт сдвигов то да, щас досмотрел, что цикл сработает от нуля и до трёх. А значений то, по сути дела, четыре, поэтому да, мой косяк, не учёл.

    - - - Добавлено - - -

    Цитата Сообщение от VVWVV Посмотреть сообщение
    Лучше использовать возвращаемое значение для ошибок, а сам результат возвращать через ссылку. Кроме того, можно использовать упакованный массив для октетов, а уже после возвращать целую ячейку. Значение лучше конвертировать без функции strval - это намного дешевле вызова функции.

    1. stock IPv4ToInteger(&dst, const src[]) {
    2. new i = 0, chr, buf[3 char], j = 0, val = 0;
    3. while ((chr = src[i++]) != EOS) {
    4. switch (chr) {
    5. case '0'..'9': {
    6. val = val * 10 + chr - '0';
    7. if (val > 255) {
    8. return -1;
    9. }
    10. }
    11. case '.': {
    12. if (j > 3) {
    13. return -1;
    14. }
    15. buf{j++} = val;
    16. val = 0;
    17. }
    18. default: {
    19. return -1;
    20. }
    21. }
    22. }
    23. dst = buf[0] | val;
    24. return 0;
    25. }
    Имею вопрос к окончательному выражению: что по итогу будет вмещать в себя нулевая ячейка массива buf? По сути дела, первое из значений(допустим 192). Потом, если мы "склеим" двоичное представление этого числа с переменной val(которая, допустим, хранит 255), то получим 255:
    (192: 0000 0000 0000 0000 0000 0000 1100 0000)
    (255: 0000 0000 0000 0000 0000 0000 1111 1111)
    (255: 0000 0000 0000 0000 0000 0000 1111 1111).
    Хотя, по сути дела, должны были получить величину, составленную из чисел. Или всё же, имеется ввиду, что обращение идёт непосредственно к неупакованной ячейке массива, размером в 4 байта, которая, по сути, на момент данной операции должна вмещать в себя три числа и, в последующем, должна склеить ещё последнее, хранящееся в переменной val? И почему именно switch? Разве условие вида: '0' <= chr <= '9' не будет обрабатываться быстрее?
    Последний раз редактировалось Shaolinka; 05.04.2023 в 19:56.

  8. #6
    Аватар для Shaolinka
    Пользователь

    Статус
    Оффлайн
    Регистрация
    19.01.2020
    Сообщений
    69
    Репутация:
    8 ±
    Решил, всё же, проверить алгоритм конвертации из числа в строку. По итогу, работает он слегка не так как нужно, а именно записывает данные с конца, а не с начала. Первые числа мы ведь записываем в старшие байты, потому сдвигаться изначально нужно до них. Я лично решил это следующим образом: добавив переменную pos с изначальным значением -1(потому что использовал преинкремент), которая выступала в роли индекса ячейки, куда пойдёт запись нужного числа. Плюс к тому, было принято решение использовать декремент в цикле, дабы замутить некое подобие обратного цикла, который будет сдвигаться должным образом: с начала и до конца. Естественно, можно замутить и инкремент в цикле, но, тогда уж, по умолчанию, переменная pos должна принимать в себя значение 4(это с учётом, если будет использоваться предекремент при записи в ячейку).

  9. #7
    Аватар для punkochel
    Пользователь

    Статус
    Оффлайн
    Регистрация
    08.12.2018
    Адрес
    Россия
    Сообщений
    146
    Репутация:
    25 ±
    Я делал функции, чтобы они были совместимы с функциями MySQL: INET_NTOA и INET_ATON.
    Чтобы сделать конвертацию с обратной записью байтов, достаточно изменить порядок элементов массива:
    1. // return part[3] | (part[2] << 8) | (part[1] << 16) | (part[0] << 24);
    2. return part[0] | (part[1] << 8) | (part[2] << 16) | (part[3] << 24);
    3.  
    4. // format(result, 15+1, "%d.%d.%d.%d", part[3], part[2], part[1], part[0]);
    5. format(result, 15+1, "%d.%d.%d.%d", part[0], part[1], part[2], part[3]);

  10. Пользователь сказал cпасибо:
    execution (28.09.2023)
 

 

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •