Сейчас на форуме: jinoweb (+6 невидимых)

 eXeL@B —› Программирование —› ByteToHex
. 1 . 2 . 3 . 4 . 5 . >>
Посл.ответ Сообщение


Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 07 июля 2018 22:06 · Поправил: Isaev
· Личное сообщение · #1

Не подскажите максимально быстрый способ перевода? Помню во времена доса на асме буквально из десятка байт был финт для этого дела, может помнит кто?
А то дельфовый IntToHex(n, 2) сожрал всю скорость, посмотрев сырки стало сразу понятно куда)

А вообще нужно из md5 массива байт строку собрать
если делать влоб
Code:
  1. s:='';
  2. for i:=0 to 15 do
  3.   s:=s+IntToHex(Digest[i], 2);

то 10млн раз выполняются около 6 сек, хотелось бы поделить это время на 2 или 3... к слову, если вместо IntToHex просто добавлять '00', то будет 2 сек. Пробовал вместо простой конкатенации заюзать TStringBuilder, но нынче, похоже менеджер памяти уже сам вполне справляется с этим хламом и с ним только на 1 сек дольше получается. Так же пробовал выделить память сразу под всю строку и мувами загонять на места, но получается тоже дольше... в общем проблема только в переводе в hex, остальное, похоже достаточно оптимально работает

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh




Ранг: -0.7 (гость), 170thx
Активность: 0.540
Статус: Участник

Создано: 07 июля 2018 22:14
· Личное сообщение · #2

вместо строк использовать array, конкатенация дорогая операция, размер фиксирован, финализировать массив терменирующим 0.
в IntToHex и так скорее всего ассемблер.




Ранг: 2014.5 (!!!!), 1278thx
Активность: 1.340.25
Статус: Модератор
retired

Создано: 07 июля 2018 22:18
· Личное сообщение · #3

А зачем генерить 10 млн МД5 строк?




Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 07 июля 2018 22:24
· Личное сообщение · #4

shellstorm говорю же потестил, особо без разницы
IntToHex то на асм, но он не заточен под байт и прыгает по куче подпрограмм, а в основе вот такой код
Code:
  1. procedure CvtInt64W;
  2. IN:
  3.     EAX:  Address of the int64 value to be converted to text
  4.     ESI:  Ptr to the right-hand side of the widechar output buffer:  LEA ESI, WStrBuf[32]
  5.     ECX:  Base for conversion: 10 or 16
  6.     EDX:  Precision: zero padded minimum field width
  7.   OUT:
  8.     ESI:  Ptr to start of converted widechar text (not start of buffer)
  9.     ECX:  Character length of converted text
  10. }
  11. asm  // StackAlignSafe
  12.         OR      CL, CL
  13.         JNZ     @start
  14.         MOV     ECX, 10
  15.         TEST    [EAX + 4], $80000000
  16.         JZ      @start
  17.         PUSH    [EAX + 4]
  18.         PUSH    [EAX]
  19.         MOV     EAX, ESP
  20.         NEG     [ESP]              // negate the value
  21.         ADC     [ESP + 4],0
  22.         NEG     [ESP + 4]
  23.         CALL    @start
  24.         INC     ECX
  25.         MOV     [ESI-2].Word, '-'
  26.         SUB     ESI, 2
  27.         ADD     ESP, 8
  28.         JMP     @done
  29.  
  30. @start:
  31.         PUSH    ESI
  32.         SUB     ESP, 4
  33.         FNSTCW  [ESP+2].Word       // save
  34.         FNSTCW  [ESP].Word         // scratch
  35.         OR      [ESP].Word, $0F00  // trunc toward zero, full precision
  36.         FLDCW   [ESP].Word
  37.  
  38.         MOV     [ESP].Word, CX
  39.         FLD1
  40.         TEST    [EAX + 4], $80000000 // test for negative
  41.         JZ      @ld1                 // FPU doesn't understand unsigned ints
  42.         PUSH    [EAX + 4]            // copy value before modifying
  43.         PUSH    [EAX]
  44.         AND     [ESP + 4], $7FFFFFFF // clear the sign bit
  45.         PUSH    $7FFFFFFF
  46.         PUSH    $FFFFFFFF
  47.         FILD    [ESP + 8].QWord     // load value
  48.         FILD    [ESP].QWord
  49.         FADD    ST(0), ST(2)        // Add 1.  Produces unsigned $80000000 in ST(0)
  50.         FADDP   ST(1), ST(0)        // Add $80000000 to value to replace the sign bit
  51.         ADD     ESP, 16
  52.         JMP     @ld2
  53. @ld1:
  54.         FILD    [EAX].QWord         // value
  55. @ld2:
  56.         FILD    [ESP].Word          // base
  57.         FLD     ST(1)
  58. @loop:
  59.         SUB     ESI, 2
  60.         FPREM                       // accumulator mod base
  61.         FISTP   [ESI].Word
  62.         FDIV    ST(1), ST(0)        // accumulator := acumulator / base
  63.         MOV     AX, [ESI].Word      // overlap long division op with int ops
  64.         ADD     AX, '0'
  65.         CMP     AX, '0'+10
  66.         JB      @store
  67.         ADD     AX, ('A'-'0')-10
  68. @store:
  69.         MOV     [ESI].Word, AX
  70.         FLD     ST(1)           // copy accumulator
  71.         FCOM    ST(3)           // if accumulator >= 1.0 then loop
  72.         FSTSW   AX
  73.         SAHF
  74.         JAE @loop
  75.  
  76.         FLDCW   [ESP+2].Word
  77.         ADD     ESP,4
  78.  
  79.         FFREE   ST(3)
  80.         FFREE   ST(2)
  81.         FFREE   ST(1);
  82.         FFREE   ST(0);
  83.  
  84. @zeropad:
  85.         POP     ECX             // original ESI
  86.         SUB     ECX,ESI
  87.         SHR     ECX, 1          // ECX = char length of converted string
  88.         OR      EDX,EDX
  89.         JS      @done
  90.         SUB     EDX,ECX
  91.         JBE     @done           // output longer than field width = no pad
  92.         SUB     ESI,EDX
  93.         MOV     AX,'0'
  94.         SUB     ESI,EDX
  95.         ADD     ECX,EDX
  96.         JMP     @z
  97. @zloop: MOV     [ESI+EDX*2].Word,AX
  98. @z:     DEC     EDX
  99.         JNZ     @zloop
  100.         MOV     [ESI].Word,AX
  101. @done:
  102. end;

т.ч. бред его для байта юзать... пойду копаться в старых асмовских сырках своих, может где-то, осталось)

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh




Ранг: 71.2 (постоянный), 33thx
Активность: 0.050.12
Статус: Участник

Создано: 07 июля 2018 22:24 · Поправил: kunix
· Личное сообщение · #5

А закешировать IntToHex(Digest[i], 2)?
Иначе говоря, вместо IntToHex(Digest[i], 2) писать byte_to_hex[Digest[i]], где byte_to_hex - массив строк.

Лол, а зачем там операции с плавающей точкой?




Ранг: 338.5 (мудрец), 349thx
Активность: 2.112.42
Статус: Участник

Создано: 07 июля 2018 22:25 · Поправил: difexacaw
· Личное сообщение · #6

Проиндексировать, если всего по одному символу выводить [0%F], то можно без использования памяти под массив обойтись, загрузить его в регистры и там индексировать. Быстрее врядле как получится.

Добавлено спустя 6 минут
Isaev

Так ведь в 7 у вас плавающая точка. Для int32 в васме можно подсмотреть, там макрос есть:

Code:
  1.   @@:
  2.     mov eax, esi            ; we're going to work on AL
  3.     and al, 00001111b       ; mask out high nibble
  4.  
  5.     cmp al,10
  6.     sbb al,69h
  7.     das
  8.  
  9.     mov [edx + ecx], al     ; store the asciihex(AL) in the string
  10.     shr esi, 4              ; next nibble
  11.     dec ecx                 ; decrease counter (one byte less than dec cl :-)
  12.     jns @B                  ; eat them if there's any more


А есчо я по суркам 2к поиск прогнал, есть такое:

Code:
  1. // this always fills '0' to empty digits
  2. // caller has to make sure sz has cdigit+1 of buffer
  3. void IntToHex(OUT LPTSTR sz, IN int cdigit, IN int value)
  4. {
  5.     int i, idigit;
  6.  
  7.     if (sz && value > 0 && cdigit > 0)
  8.     {
  9.         // nul terminate the buffer
  10.         sz[cdigit] = TEXT('\0');
  11.         
  12.         for (= cdigit-1; i >= 0; i--, value /= 16)
  13.         {
  14.             idigit = value%16;
  15.             if (idigit < 10)
  16.                 sz[i] = (TCHAR)idigit + TEXT('0');
  17.             else 
  18.                 sz[i] = (TCHAR)idigit - 10 + TEXT('A');
  19.         }
  20.     }
  21. }


-----
vx


| Сообщение посчитали полезным: Isaev


Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 07 июля 2018 22:33
· Личное сообщение · #7

Archer пишет:
А зачем генерить 10 млн МД5 строк?

ищу более-менее стойкую хеш-функцию для хеш-листа... подумал md5 может на деле не очень ущербна по скорости будет для этой цели...
вообще цель следующая: надо сделать самопальное хранилище для деревьев с большим количеством узлов, соответственно с поиском узлов по хешу O(1)
А хеш будет браться от массива на 255 байт со словарём в 6 символов...
вот думаю как получить меньше коллизий

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh




Ранг: 71.2 (постоянный), 33thx
Активность: 0.050.12
Статус: Участник

Создано: 07 июля 2018 22:36
· Личное сообщение · #8

Если честно, крипто-хеши брать для подобных целей глупо.
Наверняка можно намного быстрее все решить.




Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 07 июля 2018 22:45 · Поправил: Isaev
· Личное сообщение · #9

kunix ну давай, свежую идею... может я слишком заморачиваюсь на самом деле и нужен свежий взгляд со стороны... Эти массивы (от которых я хотел хеш хранить) храниться сами не будут, т.к. это слишком много памяти, а при составлении дерева нужно быстро определить была ли уже в нём эта вершина (т.е. если была соединяемся с ней, если не было, то добавляем)
Как можно к решению подойти иначе?

вместо md5 может и правильнее использовать полиномиальный хеш, но его генерация думаю будет заметно дороже... хотя надо тестить всё

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh




Ранг: -0.7 (гость), 170thx
Активность: 0.540
Статус: Участник

Создано: 07 июля 2018 22:47 · Поправил: shellstorm
· Личное сообщение · #10

любой приличный компилятор оптимизирует это решение лучше того высера из рантайма делфи.
в делфи x64 clang компилирует, должен нормально оптимизировать.
Code:
  1. #include <iostream>
  2. int main()
  3. {
  4.     const char* hex_table = "0123456789ABCDEF";
  5.     for(auto i = 0; i != 16; i++)
  6.     {
  7.        здесь заполняем массив result[index] := hex_table[byte_i % 16];
  8.         std::cout << hex_table[% 16] << std::endl;
  9.     }
  10.     return 0;
  11. }


hash_map давно изобрели, md5 здесь точно не нужен.




Ранг: 271.4 (наставник), 331thx
Активность: 0.321.49
Статус: Участник

Создано: 07 июля 2018 22:50
· Личное сообщение · #11

Чем-то типа этого пользуюсь, размазываешь байт по 16битам с учетом little endian, складываешь с символами нулей, если надо докручиваешь оба до abcdef. При желании можно эффективней набивать регистр, особенно 64битный, но это уже наверное слишком.
Code:
  1.                         movzx eax,BYTE[input]
  2.                         rol ax,4 + 8
  3.                         shr ah,4
  4.                         add eax,'00'
  5.                         .if ah>'9'
  6.                               add ah,'a'-'9'-1
  7.                         .endif
  8.                         .if al>'9'
  9.                               add al,'a'-'9'-1
  10.                         .endif
  11.                         mov [output],ax
  12.                         ret
  13.  
  14. input    db 0xB4
  15. output   rw 1


-----
2 оттенка серого


| Сообщение посчитали полезным: Isaev


Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 07 июля 2018 22:57
· Личное сообщение · #12

shellstorm пишет:
hash_map давно изобрели, md5 здесь точно не нужен.

его изобрели везде, кроме дельфи)
там можно заюзать Generics.Collections.TDictionary<TKey,TValue>
где TKey должен быть уникальным... у меня это массив, массив мы ему загнать не можем, потому надо взять от него хеш... так ведь?)

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh




Ранг: 71.2 (постоянный), 33thx
Активность: 0.050.12
Статус: Участник

Создано: 07 июля 2018 22:58 · Поправил: kunix
· Личное сообщение · #13

Isaev, думаю, медленее MD5 будет только другой крипто-хеш.
Блин, если исходные ключи не сохраняются, то при коллизии все сломается, так?.
Тогда я бы все же брал крипто.



Ранг: -0.7 (гость), 170thx
Активность: 0.540
Статус: Участник

Создано: 07 июля 2018 23:05
· Личное сообщение · #14

Isaev пишет: где TKey должен быть уникальным...

не настолько уникальным, достаточно операций с константой.
первый попавший исходный код, скорее всего сомнительный, но для понимания сути достаточно https://github.com/ffTsuzuku/Hash_Map/blob/master/hash.c коллизии конечно возможны, но они и в md5 есть, приходится выбирать соотношения надежности, потребления памяти, и скорости, иначе так до sha512 можно добраться.




Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 07 июля 2018 23:17 · Поправил: Isaev
· Личное сообщение · #15

kunix пишет:
Блин, если исходные ключи не сохраняются, то при коллизии все сломается, так?.

именно, потому md5, наверное, тоже бредовая затея, но я погоняю, мне интересно на сколько часто на таком объёме там могут коллизии проскакивать

shellstorm пишет:
не настолько уникальным, достаточно операций с константой.

это я не понял
shellstorm пишет:
первый попавший исходный код, скорее всего сомнительный

ну он под маленький словарь просто, на 5к элементов... может на нём он и не даёт коллизий

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh




Ранг: -0.7 (гость), 170thx
Активность: 0.540
Статус: Участник

Создано: 07 июля 2018 23:22 · Поправил: shellstorm
· Личное сообщение · #16

Isaev пишет: это я не понял

подобранной константой добавляют соль.
Code:
  1. int hash_function(struct hash_table *h_table, char *key)
  2. {
  3.  
  4.   unsigned long hash = 5381;
  5.   int c;
  6.  
  7.   while((= *key++))
  8.     hash = ((hash << 5) + hash) + c;
  9.  
  10.   return <b>hash % 5381</b>;
  11.  
  12. }


Isaev пишет: может на нём он и не даёт коллизий

зависит от входных данных, допустим для построения cfg графа PE файла не нужны никакие md5. важны как сами данные, так и их количественное разнообразие, в описаниях алгоритма указывается вероятность коллизии, подбирайте под свой случай, поскольку даже md5 не всегда достаточно, смотря какие требования к отказоустойчивости.




Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 07 июля 2018 23:45
· Личное сообщение · #17

shellstorm пишет:
зависит от входных данных

Я писал выше, 255 байт с 6 возможными различными значениями(любыми)

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh




Ранг: -0.7 (гость), 170thx
Активность: 0.540
Статус: Участник

Создано: 08 июля 2018 00:24 · Поправил: shellstorm
· Личное сообщение · #18

Isaev пишет: Я писал выше, 255 байт с 6 возможными различными значениями(любыми)

просмотрел, но в целом понятно и для этого случая давно изобрели суффиксные\префиксные деревья, и работать с ними с адекватной скоростью. но их в делфи скорее всего тоже не завезли.




Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 08 июля 2018 00:44
· Личное сообщение · #19

shellstorm пишет:
для этого случая давно изобрели суффиксные\префиксные деревья, и работать с ними с адекватной скоростью.
но их в делфи скорее всего тоже не завезли.

Это всё мелочи, главное знать как это называется. Логика гуглится, а дальше дело техники... всё имплементируемо!
Спасибо за наводку!

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh




Ранг: 315.1 (мудрец), 631thx
Активность: 0.30.33
Статус: Модератор
CrackLab

Создано: 08 июля 2018 00:53
· Личное сообщение · #20

Всё там завезли
Isaev пишет:
там можно заюзать Generics.Collections.TDictionary<TKey,TValue>
где TKey должен быть уникальным... у меня это массив, массив мы ему загнать не можем, потому надо взять от него хеш... так ведь?)

что значит загнать массив не можем?
там можно хранить все что угодно, хоть структуры, хоть классы, хоть чёрта лысого.

Isaev пишет:
соответственно с поиском узлов по хешу O(1)

TDictionary это и есть хештаблица с доступом к элементу O(1)

| Сообщение посчитали полезным: Isaev

Ранг: -0.7 (гость), 170thx
Активность: 0.540
Статус: Участник

Создано: 08 июля 2018 01:19
· Личное сообщение · #21

Isaev пишет: Логика гуглится, а дальше дело техники...

если из 255 байт меняется только шесть рандомных, задача и сводится к шести уникальным байтам которые извлекаются от вершины дерева - первой строки, эти шесть байт можно разбить на три и уже на трех байтах строить корпуса. задача ни о чем.




Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 08 июля 2018 01:20 · Поправил: Isaev
· Личное сообщение · #22

SReg пишет:
там можно хранить все что угодно

Вот именно, что хранить, а хранить я его совсем не планировал...
Но при таком подходе сам массив же тоже будет храниться и жрать много памяти!
Глянул в этом словаре хеш функцию, я правильно понимаю, в качестве хеша используется указатель на сам элемент?

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh




Ранг: -0.7 (гость), 170thx
Активность: 0.540
Статус: Участник

Создано: 08 июля 2018 01:22
· Личное сообщение · #23

Isaev пишет: Или там от ключа только хеш хранится?

хеш это ключ через который получаешь доступ к данным через указатель.



Ранг: 315.1 (мудрец), 631thx
Активность: 0.30.33
Статус: Модератор
CrackLab

Создано: 08 июля 2018 01:57
· Личное сообщение · #24

Isaev пишет:
сам массив же будет храниться и жрать много памяти?

непонятно, что за массивы ты хранить собрался.
10 лямов мд5 хешей, с быстрым доступом и чтоб память не жрало? может нужно смотреть в сторону баз данных?



Ранг: 431.7 (мудрец), 390thx
Активность: 0.730.32
Статус: Участник

Создано: 08 июля 2018 03:19 · Поправил: dosprog
· Личное сообщение · #25

Isaev пишет:
Не подскажите максимально быстрый способ перевода? Помню во времена доса на асме буквально из десятка байт был финт для этого дела, может помнит кто?



Вариант 1
Code:
  1. hex_table db  '0123456789ABCDEF'
  2. al2hex proc
  3.    ;---------------------------
  4.    ;bin AL -> Hex AX (можно делать stosw в строку)
  5.    ;---------------------------
  6.    push bx
  7.    mov bx,offset hex_table
  8.    mov ah,al
  9.    and al,0Fh
  10.    segcs xlat
  11.    xchg ah,al
  12.    mov cl,4
  13.    shr al,cl
  14.    segcs xlat
  15.    pop bx
  16.    ret
  17. al2hex endp
Вариант 2
Code:
  1. al2hex2 proc 
  2.      ;---------------------------
  3.      ;bin AL -> Hex ES:[DI]
  4.      ;---------------------------
  5.      mov bp,ax
  6.      mov cl,4
  7. @@l1:  
  8.      mov ax,bp
  9.      ror ax,cl     
  10.      and al,0Fh
  11.      add al,'0'  
  12.      cmp al,'9'  
  13.      jbe @@l2 
  14.      add al,7
  15. @@l2:  
  16.      cld 
  17.      stosb       
  18.      sub cl,4
  19.      jnc @@l1             
  20.      ret
  21. al2hex2    endp





Ранг: 2014.5 (!!!!), 1278thx
Активность: 1.340.25
Статус: Модератор
retired

Создано: 08 июля 2018 06:55
· Личное сообщение · #26

Хеш-таблицы - это хорошо. А зачем для этого 10 млн строк? Зачем вообще для этого строки? Когда хранение именно в байтах занимает в 2 раза меньше памяти и быстрее сравнивается?

Для хеш-таблиц выбор именно криптографического хеша не всегда оправдан. И да, к коллизиям надо быть готовым в любом случае, неважно, какой хеш. Пытаться избавиться от коллизий, повышая размерность и стойкость хеша, занятие обычно неблагодарное.




Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 08 июля 2018 08:18 · Поправил: Isaev
· Личное сообщение · #27

SReg пишет:
непонятно, что за массивы ты хранить собрался.

Я то как раз собрался их НЕ хранить...
а в TKey они будут храниться целиком в памяти! В том и проблема

PS: потестил... мало того, он умножает занятое место ещё на 2 для хранения... т.е. если 1млн ключей по 255 байт занимал бы 255мб, то он хранит это в 530мб

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh





Ранг: 216.9 (наставник), 85thx
Активность: 0.310.15
Статус: Участник
X-Literator

Создано: 08 июля 2018 14:53 · Поправил: Crawler
· Личное сообщение · #28

В С++ - это без оптимизации и в дебаг-режиме - занимает 7 секунд. В релизе с флагом /O2 занимает 1.5 секунды. Может, написать статическую библиотеку и встроить код (я хер знает, можно ли это в дельфи ?

А, сука, я там \n поставил по старой привычке переводить строку. Было бы быстрее без него раза в два
Значения массива для тестирования инициализировались rand()%255.

1.

2.



* Чтобы оценить поточнее, поставил float и убрал перенос строки - получилось 1.066 секунды. Это учитывая, что вместо sprintf() можно встроить какой-нибудь эффективный инлайн-кусочек.

** Версия с itoa(value, buff[i], 16); выполняется за ~0.35-0.5 секунд.

-----
Харе курить веники и нюхать клей, к вам едет из Америки бог Шива, и он еврей.




Ранг: 431.7 (мудрец), 390thx
Активность: 0.730.32
Статус: Участник

Создано: 08 июля 2018 16:00
· Личное сообщение · #29

sprintf() очень сложная функция. Работает медленно по определению.




Ранг: 216.9 (наставник), 85thx
Активность: 0.310.15
Статус: Участник
X-Literator

Создано: 08 июля 2018 16:05 · Поправил: Crawler
· Личное сообщение · #30

dosprog, поэтому ниже я дописал, что протестировал с itoa(), получилось 0.35 секунд ;)

Можно посмотреть исходники itoa(), убрать оттуда работу с разными основаниями (подставить сразу же 16), и выиграть ещё где-то 0.1 секунды.

-----
Харе курить веники и нюхать клей, к вам едет из Америки бог Шива, и он еврей.



. 1 . 2 . 3 . 4 . 5 . >>
 eXeL@B —› Программирование —› ByteToHex
:: Ваш ответ
Жирный  Курсив  Подчеркнутый  Перечеркнутый  {mpf5}  Код  Вставить ссылку 
:s1: :s2: :s3: :s4: :s5: :s6: :s7: :s8: :s9: :s10: :s11: :s12: :s13: :s14: :s15: :s16:


Максимальный размер аттача: 500KB.
Ваш логин: german1505 » Выход » ЛС
   Для печати Для печати