11 (изменено: Freeman, 19.08.2023 в 20:13)

Re: Строки и Unicode

Freeman пишет:

Есть предположение, что в Колибри только в ядре Unicode нормальный, а качество остального сомнительно, поскольку никем не используется.

Freeman пишет:

Как бы не оказалось, что единственной программой, использующей Unicode-возможности Колибри, является наш обновленный "Hello, world!", инициализирующий заголовок консоли путем к программе в UTF-8, формируемым ядром.

Ну вообще есть приложения FB2READ и TXTREAD, а ещё web-браузер, которые как бы поддерживают в какой-то степени юникод.

Freeman пишет:

выяснилось, что ядро уже давно поддерживает Unicode для файловой и графической подсистем, а системный шрифт содержит символы всех языков на латинице и кириллице

Возьмём для примера UTF-8 encoded sample plain-text file

текст из того файла(продублирую здесь на всякий случай):
UTF-8 encoded sample plain-text file
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾

Markus Kuhn [ˈmaʳkʊs kuːn] <http://www.cl.cam.ac.uk/~mgk25/> — 2002-07-25 CC BY


The ASCII compatible UTF-8 encoding used in this plain-text file
is defined in Unicode, ISO 10646-1, and RFC 2279.


Using Unicode/UTF-8, you can write in emails and source code things such as

Mathematics and sciences:

  ∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i),      ⎧⎡⎛┌─────┐⎞⎤⎫
                                            ⎪⎢⎜│a²+b³ ⎟⎥⎪
  ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β),    ⎪⎢⎜│───── ⎟⎥⎪
                                            ⎪⎢⎜⎷ c₈   ⎟⎥⎪
  ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ,                   ⎨⎢⎜       ⎟⎥⎬
                                            ⎪⎢⎜ ∞     ⎟⎥⎪
  ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫),      ⎪⎢⎜ ⎲     ⎟⎥⎪
                                            ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪
  2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm     ⎩⎣⎝i=1    ⎠⎦⎭

Linguistics and dictionaries:

  ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn
  Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]

APL:

  ((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈

Nicer typography in plain text files:

  ╔══════════════════════════════════════════╗
  ║                                          ║
  ║   • ‘single’ and “double” quotes         ║
  ║                                          ║
  ║   • Curly apostrophes: “We’ve been here” ║
  ║                                          ║
  ║   • Latin-1 apostrophe and accents: '´`  ║
  ║                                          ║
  ║   • ‚deutsche‘ „Anführungszeichen“       ║
  ║                                          ║
  ║   • †, ‡, ‰, •, 3–4, —, −5/+5, ™, …      ║
  ║                                          ║
  ║   • ASCII safety test: 1lI|, 0OD, 8B     ║
  ║                      ╭─────────╮         ║
  ║   • the euro symbol: │ 14.95 € │         ║
  ║                      ╰─────────╯         ║
  ╚══════════════════════════════════════════╝

Combining characters:

  STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑

Greek (in Polytonic):

  The Greek anthem:

  Σὲ γνωρίζω ἀπὸ τὴν κόψη
  τοῦ σπαθιοῦ τὴν τρομερή,
  σὲ γνωρίζω ἀπὸ τὴν ὄψη
  ποὺ μὲ βία μετράει τὴ γῆ.

  ᾿Απ᾿ τὰ κόκκαλα βγαλμένη
  τῶν ῾Ελλήνων τὰ ἱερά
  καὶ σὰν πρῶτα ἀνδρειωμένη
  χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!

  From a speech of Demosthenes in the 4th century BC:

  Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,
  ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς
  λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ
  τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿
  εἰς τοῦτο προήκοντα,  ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ
  πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν
  οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,
  οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν
  ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον
  τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι
  γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν
  προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους
  σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ
  τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ
  τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς
  τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.

  Δημοσθένους, Γ´ ᾿Ολυνθιακὸς

Georgian:

  From a Unicode conference invitation:

  გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო
  კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,
  ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს
  ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,
  ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება
  ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,
  ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.

Russian:

  From a Unicode conference invitation:

  Зарегистрируйтесь сейчас на Десятую Международную Конференцию по
  Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.
  Конференция соберет широкий круг экспертов по  вопросам глобального
  Интернета и Unicode, локализации и интернационализации, воплощению и
  применению Unicode в различных операционных системах и программных
  приложениях, шрифтах, верстке и многоязычных компьютерных системах.

Thai (UCS Level 2):

  Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese
  classic 'San Gua'):

  [----------------------------|------------------------]
    ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช  พระปกเกศกองบู๊กู้ขึ้นใหม่
  สิบสองกษัตริย์ก่อนหน้าแลถัดไป       สององค์ไซร้โง่เขลาเบาปัญญา
    ทรงนับถือขันทีเป็นที่พึ่ง           บ้านเมืองจึงวิปริตเป็นนักหนา
  โฮจิ๋นเรียกทัพทั่วหัวเมืองมา         หมายจะฆ่ามดชั่วตัวสำคัญ
    เหมือนขับไสไล่เสือจากเคหา      รับหมาป่าเข้ามาเลยอาสัญ
  ฝ่ายอ้องอุ้นยุแยกให้แตกกัน          ใช้สาวนั้นเป็นชนวนชื่นชวนใจ
    พลันลิฉุยกุยกีกลับก่อเหตุ          ช่างอาเพศจริงหนาฟ้าร้องไห้
  ต้องรบราฆ่าฟันจนบรรลัย           ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ

  (The above is a two-column text. If combining characters are handled
  correctly, the lines of the second column should be aligned with the
  | character above.)

Ethiopian:

  Proverbs in the Amharic language:

  ሰማይ አይታረስ ንጉሥ አይከሰስ።
  ብላ ካለኝ እንደአባቴ በቆመጠኝ።
  ጌጥ ያለቤቱ ቁምጥና ነው።
  ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።
  የአፍ ወለምታ በቅቤ አይታሽም።
  አይጥ በበላ ዳዋ ተመታ።
  ሲተረጉሙ ይደረግሙ።
  ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።
  ድር ቢያብር አንበሳ ያስር።
  ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።
  እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።
  የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።
  ሥራ ከመፍታት ልጄን ላፋታት።
  ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።
  የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።
  ተንጋሎ ቢተፉ ተመልሶ ባፉ።
  ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።
  እግርህን በፍራሽህ ልክ ዘርጋ።

Runes:

  ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ

  (Old English, which transcribed into Latin reads 'He cwaeth that he
  bude thaem lande northweardum with tha Westsae.' and means 'He said
  that he lived in the northern land near the Western Sea.')

Braille:

  ⡌⠁⠧⠑ ⠼⠁⠒  ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌

  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞
  ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎
  ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂
  ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙
  ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑
  ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲

  ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲

  ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹
  ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞
  ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕
  ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹
  ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎
  ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎
  ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳
  ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞
  ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲

  (The first couple of paragraphs of "A Christmas Carol" by Dickens)

Compact font selection example text:

  ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789
  abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ
  –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд
  ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა

Greetings in various languages:

  Hello world, Καλημέρα κόσμε, コンニチハ

Box drawing alignment tests:                                          █
                                                                      ▉
  ╔══╦══╗  ┌──┬──┐  ╭──┬──╮  ╭──┬──╮  ┏━━┳━━┓  ┎┒┏┑   ╷  ╻ ┏┯┓ ┌┰┐    ▊ ╱╲╱╲╳╳╳
  ║┌─╨─┐║  │╔═╧═╗│  │╒═╪═╕│  │╓─╁─╖│  ┃┌─╂─┐┃  ┗╃╄┙  ╶┼╴╺╋╸┠┼┨ ┝╋┥    ▋ ╲╱╲╱╳╳╳
  ║│╲ ╱│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╿ │┃  ┍╅╆┓   ╵  ╹ ┗┷┛ └┸┘    ▌ ╱╲╱╲╳╳╳
  ╠╡ ╳ ╞╣  ├╢   ╟┤  ├┼─┼─┼┤  ├╫─╂─╫┤  ┣┿╾┼╼┿┫  ┕┛┖┚     ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
  ║│╱ ╲│║  │║   ║│  ││ │ ││  │║ ┃ ║│  ┃│ ╽ │┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▎
  ║└─╥─┘║  │╚═╤═╝│  │╘═╪═╛│  │╙─╀─╜│  ┃└─╂─┘┃  ░░▒▒▓▓██ ┊  ┆ ╎ ╏  ┇ ┋ ▏
  ╚══╩══╝  └──┴──┘  ╰──┴──╯  ╰──┴──╯  ┗━━┻━━┛  ▗▄▖▛▀▜   └╌╌┘ ╎ ┗╍╍┛ ┋  ▁▂▃▄▅▆▇█
                                               ▝▀▘▙▄▟

и попробуем открыть в какой-нибудь программе из-под KolibriOS.
Но там похоже используются не системные шрифты.

Хорошо, ради интереса сделаем такой тестовый пример с кусочками текста из приведённого выше файла и будем выводить с помощью системной функции

program U_Test;

uses
  KolibriOS;

const
  DEFAULT_CURRENT_Y = 10;

var
  WndLeft, WndTop, WndWidth, WndHeight: Integer;
  CurrentY: LongInt;

procedure Print(Title, Text: PAnsiChar);
begin
// draw Title as CP866
  DrawText(4, CurrentY, Title, $00000070, $00FFFFFF, DT_ZSTRING + DT_CP866_8x16, 0);
  Inc(CurrentY, 16);
// draw Text as UTF-8
  DrawText(8, CurrentY, Text, $00000000, $00FFFFFF, DT_ZSTRING + DT_UTF8_8x16, 0);
  Inc(CurrentY, 24)
end;

procedure Test;
begin
  Print('Linguistics and dictionaries:', 'ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn');
  Print('Mathematics and sciences:', '∮ E⋅da = Q,  n → ∞, ∑ f(i) = ∏ g(i)');
  Print('APL:', '((V⍳V)=⍳⍴V)/V←,V    ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈');
  Print('Nicer typography in plain text files:', '‚deutsche‘ „Anführungszeichen“');
  Print('Combining characters:', 'STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑');
  Print('Greek (in Polytonic):', 'Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι');
  Print('Georgian:', 'გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო');
  Print('Russian:', 'Зарегистрируйтесь сейчас на Десятую Международную Конференцию по');
  Print('Thai (UCS Level 2):', 'แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช');
  Print('Ethiopian:', 'ሰማይ አይታረስ ንጉሥ አይከሰስ።');
  Print('Greetings in various languages:', 'Hello world, Καλημέρα κόσμε, コンニチハ');
end;

begin
  with GetScreenSize do
  begin
    WndWidth := 640;
    WndHeight := 480;
    WndLeft := (Width - WndWidth) div 2;
    WndTop := (Height - WndHeight) div 2;
  end;

  while True do
    case WaitEvent of
      REDRAW_EVENT:
        begin
          BeginDraw;
          DrawWindow(WndLeft, WndTop, WndWidth, WndHeight, 'U - test', $00FFFFFF,
            WS_SKINNED_SIZABLE + WS_CLIENT_COORDS + WS_CAPTION, CAPTION_MOVABLE);
          CurrentY := DEFAULT_CURRENT_Y;
          Test;
          EndDraw;
        end;
      KEY_EVENT:
        GetKey;
      BUTTON_EVENT:
        if GetButton.ID = 1 then
          Break;
    end;
end.

Скриншот примера:
misc.php?action=pun_attachment&amp;item=91&amp;download=0
Прилагаю также уже скомпилированный пример U_TEST.KEX
Кстати, этот пример можно запустить в эмуляторе KEm, в нём как и в современном ядре KolibriOS поддерживается вывод UTF-8 и UTF-16LE.

Freeman: Я, похоже, испортил это сообщение. Не туда нажал. Восстановил исходник из архива, а новый текст пропал. sad

Post's attachments

Иконка вложений U_TEST.KEX 1.43 Кб, 76 скачиваний с 2021-03-19 

U_TEST.PNG, 17.6 Кб, 641 x 481
U_TEST.PNG 17.6 Кб, 67 скачиваний с 2021-03-19 

12

Re: Строки и Unicode

0CodErr пишет:

На каком этапе сейчас находится разработка строковых функций(ведь это давно было запланировано)?

На этапе ожидания Кантора, в состоянии «а воз и ныне там». big_smile

13

Re: Строки и Unicode

Freeman: Я, похоже, испортил это сообщение. Не туда нажал. Восстановил исходник из архива, а новый текст пропал. sad

Там было примерно это:

По поводу структуры StrRec

В Delphi 5 она, вроде бы, такая:

  StrRec = packed record
    allocSiz: Longint;
    refCnt: Longint;
    length: Longint;
  end;

В Delphi 7 такая:

  StrRec = packed record
    refCnt: Longint;
    length: Longint;
  end;

Начиная с Delphi 2009 такая:

  StrRec = packed record
    codePage: Word;
    elemSize: Word;
    refCnt: Longint;
    length: Longint;
  end;

Здесь вроде как решили, что Delphi 5 не будет поддерживаться.

Не совсем понятно, зависит ли эта структура только от версии компилятора или
ещё от целевой платформы и предпочтений разработчика.
Можно ли её определять как-то по-своему?
Как будет в конечном счёте реализовано у нас(сейчас в ветке develop есть проверка на UnicodeCompiler)?

Судя по статье в новых версиях добавилось очень много нюансов.

На каком этапе сейчас находится разработка строковых функций(ведь это давно было запланировано)?

Я мог бы сделать некоторые тесты для этих функций, кое-какие уже есть, наподобие таких
var
  S, S1, S2, S3, S4, S5, S6: AnsiString;
.......  
  // Конкатенация строк
  S := S1 + S2;           // LStrCat3
  S := S + S3;            // LStrCat
  S := S + S4 + S5 + S6;  // LStrCatN
var
  S: AnsiString;
  P: PAnsiChar;
  SS, SS1: ShortString;
  A: array [0..42] of AnsiChar;
.......    
  S := P;     // LStrFromPChar
  S := SS;    // LStrFromString
  SS := P;    // CToPasStr
  S := A;     // LStrFromArray
  SS := A;    // CLenToPasStr
  SS := SS1;  // PStrCpy
  SS := S;    // LStrToString

 

14

Re: Строки и Unicode

0CodErr пишет:

Не совсем понятно, зависит ли эта структура только от версии компилятора или
ещё от целевой платформы и предпочтений разработчика.

От компилятора. Именно он формирует описанные мной структуры для строковых констант в exe-файле. В Borland не стали заморачиваться, структуры одинаковы для констант и строковых переменных.

0CodErr пишет:

Как будет в конечном счёте реализовано у нас(сейчас в ветке develop есть проверка на UnicodeCompiler)?

Я планировал заморочиться, чтобы кодовые страницы работали на всех версиях Delphi, но сейчас не могу сказать как. Забыл. Надо изучать исходники, чтобы вспомнить, а я занят другим.

15

Re: Строки и Unicode

Неспроста спросил про Delphi 5 и allocSiz в структуре.
Ведь системный аллокатор KolibriOS выделяет блок размером кратным 4096.
Было бы не очень рационально слишком часто вызывать Realloc, но если бы мы знали, что количества выделенной памяти ещё хватает, то не делали бы лишних вызовов.

Ещё в _NewAnsiString есть такой момент, учитывающий особенности аллокатора:

  // Alloc an extra null for strings with even length.  This has no actual cost
  // since the allocator will round up the request to an even size anyway.
  // All widestring allocations have even length, and need a double null terminator.
  GetMem(P, length + sizeof(StrRec) + 1 + ((length + 1) and 1));

По сути функции уже существуют в оригинальном System — их логику не нужно как-либо переделывать под KolibriOS — они уже и так работают, но нужно, как я понял, сделать лицензионно-чистые аналоги.

Вариантов, как сделать аналоги несколько, какие из них наши?

  • Изменять имена параметров и переменных

  • Использовать приведение типов наоборот, например, вместо "Byte(MyChar) := MyByte" использовать "MyChar := AnsiChar(MyByte)"

  • Заменять цикл while на repeat и наоборот

  • Вместо
    "if X then Exit;"
    использовать
    "if not X then
    begin
      <тело функции>
    end;"

  • переписать с Pascal на ассемблер и наоборот

  • изменить порядок вычислений, если это не влияет на результат

  • заменить if на case и наоборот

  • вставить или убрать дополнительные проверки

  • поменять местами порядок следования функций в модуле

  • изменить используемые структуры данных

  • заменять заинлайненный код на вызов нужной функции или наоборот, например, вместо "rep movsd + rep mosb" делать"call Move"

  • использовать Inc\Dec вместо +\- и наоборот


Если всё ещё "воз и ныне там", то могу попробовать переделать некоторые функции.
Если что-то получится — буду выкладывать сюда.
Тогда и тесты тоже сюда выложу.

16

Re: Строки и Unicode

Используя перечисленные выше способы переделал несколько функций:

  • InterlockedDecrement

  • InterlockedIncrement

  • _NewAnsiString

  • _LStrClr

  • _LStrFromPCharLen

  • _LStrFromString

  • _LStrFromChar

  • _LStrToString

  • _PStrCpy

  • __CLenToPasStr

  • __CToPasStr

  • _LStrAddRef

Вот они
type
  PStrRec = ^TStrRec;
  TStrRec = packed record
  {$IFDEF UnicodeCompiler}
    CodePage: Word;    // code page of string
    CharSize: Word;    // number of bytes per character of string
  {$ENDIF}
    RefCount: LongInt; // number of references to string
    Length: LongInt;   // number of characters of string
  end;

function InterlockedDecrement(var Value: LongInt): LongInt;
asm
  MOV  EDX, Value      // Передаем адрес переменной Value в edx
  MOV  EAX, -1         // Устанавливаем значение, на которое нужно увеличить счетчик (-1)
  LOCK XADD [EDX], EAX // Инкрементируем значение по адресу edx и сохраняем результат в eax
  DEC  EAX             // Уменьшаем eax на 1, так как InterlockedDecrement должен вернуть новое значение
end;

function InterlockedIncrement(var Value: LongInt): LongInt;
asm
  MOV  EDX, Value      // Передаем адрес переменной Value в edx
  MOV  EAX, 1          // Устанавливаем значение, на которое нужно увеличить счетчик (1)
  LOCK XADD [EDX], EAX // Инкрементируем значение по адресу edx и сохраняем результат в eax
  INC  EAX             // Увеличиваем eax на 1, так как InterlockedIncrement должен вернуть новое значение
end;

function _NewAnsiString(Length: LongInt): Pointer;
var
  P: PStrRec;
begin
  if Length > 0 then
  begin
    GetMem(P, Length + SizeOf(TStrRec) + 1 + ((Length + 1) and 1));
    Result := Pointer(PAnsiChar(P) + SizeOf(TStrRec));
    PWideChar(Result)[Length div 2] := #0;
    P.RefCount := 1;
    P.Length := Length;
  end
  else
    Result := nil;
end;

procedure _LStrClr(var S);
var
  P: PStrRec;
begin
  if Pointer(S) = nil then Exit;
  P := PStrRec(PAnsiChar(S) - SizeOf(TStrRec));
  Pointer(S) := nil;
  if P.RefCount <= 0 then Exit;
  if InterlockedDecrement(P.RefCount) <> 0 then Exit;
  FreeMem(P);
end;

procedure _LStrFromPCharLen(var Dst: AnsiString; Src: PAnsiChar; Length: Integer);
var
  P: Pointer;
begin
  P := _NewAnsiString(Length);
  if Src <> nil then
    Move(Src^, P^, Length);
  _LStrClr(Pointer(Dst));
  Pointer(Dst) := P;
end;

procedure _LStrFromString(var Dst: AnsiString; const Src: ShortString);
begin
  _LStrFromPCharLen(Dst, PAnsiChar(@Src[1]), Length(Src));
end;

// на самом деле, в оригинальной asm-версии не edx хотят сохранить,
// а передают адрес локальной переменной на стеке,
// pop edx нужно для восстановления баланса стека(можно было pop eax сделать вместо этого)
procedure _LStrFromChar(var Dst: AnsiString; Src: AnsiChar);
begin
  _LStrFromPCharLen(Dst, @Src, 1);
end;

procedure _LStrToString(var Dst: ShortString; const Src: AnsiString; MaxLen: Integer);
var
  Len: LongInt;
begin
  if Pointer(Src) <> nil then
  begin
    Len := PStrRec(@(PAnsiChar(Src)[-SizeOf(TStrRec)])).Length;
// можно по-другому: Len := Length(Src);
// или как обычно:   Len := PStrRec(PAnsiChar(Src) - SizeOf(TStrRec)).Length;
    if Len <> 0 then
    begin
      if Len > MaxLen then
         Len := MaxLen;
      Dst[0] := AnsiChar(Len);
      Move(Pointer(Src)^, Dst[1], Len);
      Exit;
    end;
  end;
  Dst[0] := #0;
end;

procedure _PStrCpy(var Dst, Src: ShortString);
begin
  Move(Src, Dst, Byte(Src[0]) + 1);
end;

procedure __CLenToPasStr(Dst: PShortString; const Src: PAnsiChar; MaxLen: Integer);
var
  i: LongInt;
begin
  if MaxLen > High(ShortString) then 
    MaxLen := High(ShortString);
  i := 0;
  while (i <= MaxLen) and (Src[i] <> #0) do
  begin
    Dst^[i + 1] := Src[i];
    Inc(i);
  end;
  {if i > 0 then
    Dec(i);} // это не надо на самом деле, лишний код в оригинале
  Byte(Dst^[0]) := i;
end;

procedure __CToPasStr(Dst: PShortString; const Src: PAnsiChar);
begin
  __CLenToPasStr(Dst, Src, High(ShortString));
end;

function _LStrAddRef(var S): Pointer;
var
  P: PStrRec;
begin
  Result := Pointer(S);
  P := PStrRec(@(PAnsiChar(S)[-SizeOf(TStrRec)]));
// или так: P := PStrRec(PAnsiChar(S) - SizeOf(TStrRec));
  if P = nil then Exit;
  if P.RefCount < 0 then Exit;
  InterlockedIncrement(P.RefCount);
end;

Иногда для получения указателя в оригинале System встречается такое:

PStrRec(Integer(s) - sizeof(StrRec))

Как лучше заменять?
Есть варианты:

PStrRec(PAnsiChar(S) - SizeOf(TStrRec))
PStrRec(@(PAnsiChar(Src)[-SizeOf(TStrRec)]))

А для получения длины можно вместо

PStrRec(Integer(s) - sizeof(StrRec)).length

использовать

Length(Src)

произойдёт вызов _LStrLen, но внутри одновременно произойдёт проверка указателя на nil, что также может быть полезным иногда.

Некоторые функции из System, возможно, никогда не вызываются.
Например, вместо, казалось бы, логичного вызова _PStrCat для
конкатенации ShortString происходит преобразование в AnsiString и обратно даже при отключении "Huge strings":

var
  SS1, SS2: ShortString;
...
  SS1 := SS1 + SS2; // LStrFromString + LStrCat + LStrToString  

Насколько знаю, функции InterlockedDecrement и InterlockedIncrement в более современных версиях называются AtomicDecrement и AtomicIncrement.
Здесь код почти как у Microsoft в kernel32.dll, только вместо регистра ecx используется edx.
 
Пока ещё не тестировал, но уже нашёл неточность в оригинальном исходнике Delphi в Pascal-версии процедуры __CLenToPasStr.
Этот код лишний, в asm-версии это не делается:

  if I > 0 then
    Dec(I);

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

17

Re: Строки и Unicode

Мне кажется, что наши функции обработки строк должны быть написаны на Паскале — для лучшей портируемости (?) и лучшей понятности и поддерживаемости другими разработчиками, которые могут влиться в команду SDK. Так что представленный вариант в целом приемлем. Понятно также, что InterlockedIncrement и InterlockedDecrement могут быть только на ассемблере.

Но! В Колибри используется кодовая страница 866 или разновидности Юникода, тогда как среда Delphi, будучи однобайтовой в случае Delphi 6/7, естественным образом позволяет вводить и обрабатывать русские строки только в кодировке 1251. Если мы стремимся к естественности разработки на Delphi, RTL должна обеспечивать прозрачное преобразование кодировок. Другого пути, кроме как хранить кодировку каждой строки, я не вижу. Если делать RTL здорового человека, конечно.

Причем, если держать в уме поддержку Юникода самой Колибри, должна быть возможность указать кодировку строк, введенных в среде Delphi и затем сформированных компилятором в коде, чтобы естественным образом иметь сообщения на других европейских языках, не только на русском. В современных версиях Delphi есть переменная DefaultSystemCodePage — можно ввести ее к нам и хранить в ней введенную в среде Delphi кодировку, установив значение 1251 по умолчанию. Тогда итальянцы, например, будут вписывать туда 1252 и иметь строки на итальянском без плясок с бубном. В этом случае должны вызываться юникодные версии функций API Колибри.

Соответственно, LStrAsg и другие процедуры должны уметь обрабатывать строковые константы, сформированные Delphi 6-2007 без кодовой страницы, подставляя в них значение DefaultSystemCodePage и выдавая строку уже с кодировкой на выходе. Это естественным образом сделает наши процедуры уникальными и снимет вопрос авторских прав.

Я также подразумеваю, что функции API, принимающие строки, должны иметь перегруженные версии с AnsiString, берущие трансляцию строк Delphi в строки API на себя. Опять же, если делать RTL здорового человека, подразумевающего отсутствие необходимости плясок с бубном. Понятно, что это нарушает переносимость API в терминах KolibriOS.lib, но Delphi есть Delphi. В принципе, поддержку AnsiString можно считать расширением Delphi поверх KolibriOS.lib, если нужна концептуальная формула.

18

Re: Строки и Unicode

Freeman пишет:

Мне кажется, что наши функции обработки строк должны быть написаны на Паскале — для лучшей портируемости (?) и лучшей понятности и поддерживаемости другими разработчиками, которые могут влиться в команду SDK.

Да, согласен. Ассемблерщиков и так в природе немного.
Если речь о портируемости, то надо подумать о правильном использовании Generic-типов(например, Integer и Cardinal).

Freeman пишет:

Так что представленный вариант в целом приемлем

Хорошо, я попробую ещё переделать какие-нибудь функции.
Вот, кстати, похоже нашёл очередную неточность в оригинальном System в Паскаль-версии функции _LStrAsg:
вместо

    end;
    InterlockedIncrement(P.refCnt);
  end;

должно быть

    end
    else
      InterlockedIncrement(P.refCnt);
  end;  

потому что в asm-версии там прыжок

               CALL    Move
                POP     EDX
                POP     EAX
                JMP     @@2
                
@@1:
           LOCK INC     [EDX-skew].StrRec.refCnt

@@2:            XCHG    EDX,[EAX]

то есть, если вызвали Move, то увеличивать счётчик не должны.

Freeman пишет:

хранить кодировку каждой строки

Чтобы что?
Не задумывался, почему CodePage не было в доюникодных версиях?
Просто в этом не было необходимости.
CodePage используется при автоматическом преобразовании в юникод и обратно с помощью встроенных функций семейства "_UStr...".
А если встроенных функций нет, то и преобразовывать нечего — будет просто лежать мёртвым грузом.

Freeman пишет:

функции API, принимающие строки, должны иметь перегруженные версии с AnsiString, берущие трансляцию строк Delphi в строки API на себя.

Freeman пишет:

В этом случае должны вызываться юникодные версии функций API Колибри.

Я не очень понял, что и куда ты собрался транслировать.
Но по-моему так себе идея.
Если передать Ansi-строку в функцию, которая и так принимает Ansi-строку, то зачем ты собрался эту строку в юникод конвертировать?
Какой в этом "великий" смысл, если Ansi-вариант и без этого будет работать?

Думаешь, почему в винде так не сделали?
Потому что и так всё работает.
Ansi-строка остаётся Ansi-строкой, не важно в какой она кодировке.
У винды тоже 100500 разных локализаций.
Но испанец в испанской винде может передать Ansi-строку в испанской кодировке как обычную Ansi-строку — всё это будет прекрасно работать без преобразования в юникод.

Freeman пишет:

В Колибри используется кодовая страница 866

Ну как сказать... Это же не запрещает использовать испанские символы при наличии испанского шрифта(да даже и без шрифта).
Просто так уж совпало, что изначально использовался шрифт с кириллическими символами.

Вообще динамические строки очень тормозные, особенно с системным аллокатором.
Как-то даже не рационально столько всего городить ради возможности писать вот так:

Str := Str1 + Str2 + Str3;

Хотя, это поможет портировать существующий код.

19

Re: Строки и Unicode

0CodErr пишет:

Не задумывался, почему CodePage не было в доюникодных версиях?
Просто в этом не было необходимости.

Понятно. В таком случае закрываю тему до просветления. После наступления оного писать на почту, чтобы открыл.