একটি উইডথ চেক বলেছিল স্ট্রিংটি কাটা নিরাপদ, কিন্তু এটি একটি কানজিকে অর্ধেক করে ফেলল।

একটি নাম টার্মিনাল টেবিলে প্রবেশ করানো হলো এবং সেটি ভেঙে বের হয়ে এল। পদবিটি ছিল 𠮷田।

প্রথম অক্ষরটি সাধারণ 吉 নয়। এটি হলো 𠮷 (U+20BB7)। এটি জাপানি পারিবারিক পদবিতে ব্যবহৃত একটি বিরল রূপ। টেবিলটি একটি কলামে ফিট করার জন্য সেলটিকে ট্রাঙ্কেট (truncate) করেছিল। এর ফলে একটি ভাঙা অক্ষর রয়ে গেল।

বাগটি কোডের মাত্র একটি লাইনে ছিল। এটি ছিল একটি অপ্টিমাইজেশন যা ইনডেক্স অনুযায়ী একটি স্ট্রিং কাটা নিরাপদ কি না তা নির্ধারণ করত।

একটি JavaScript স্ট্রিংয়ের তিনটি ভিন্ন দৈর্ঘ্য রয়েছে: • কোড ইউনিট (.length): "𠮷".length হলো 2। • কোড পয়েন্ট (Code points): [..."𠮷"].length হলো 1। • ডিসপ্লে উইডথ (Display width): 𠮷 দুটি কলাম দখল করে।

সাধারণ ইংরেজি টেক্সটের ক্ষেত্রে, এই সংখ্যাগুলো সব একই হয়। এই কাকতালীয় মিলের কারণে কোডটি নিরাপদ বলে মনে হয়।

𠮷 অক্ষরটি এই নিয়মটি ভেঙে দেয়। এর 2 টি কোড ইউনিট রয়েছে কারণ এটি একটি সারোগেট পেয়ার (surrogate pair)। এর 2 টি কলাম রয়েছে কারণ এটি একটি ওয়াইড ক্যারেক্টার (wide character)। সংখ্যাগুলো মিলে যায় (2 = 2), কিন্তু ভিন্ন ভিন্ন কারণে।

cli-table3 লাইব্রেরিটি একটি ফাস্ট পাথ (fast path) ব্যবহার করেছিল: যদি কোড ইউনিটের দৈর্ঘ্য ডিসপ্লে উইডথের সমান হয়, তবে ইনডেক্স অনুযায়ী স্ট্রিংটি কেটে ফেলো।

এটি বছরের পর বছর কাজ করেছে কারণ 漢 এর মতো সাধারণ জাপানি অক্ষরগুলোর দৈর্ঘ্য 1 এবং উইডথ 2। তারা কখনোই এই ফাস্ট পাথে পৌঁছায় না।

ফাস্ট পাথটি শুধুমাত্র 𠮷 বা ইমোজির মতো বিরল অক্ষরগুলোর ক্ষেত্রে ট্রিগার হয়। এই অক্ষরগুলোর দৈর্ঘ্য 2 এবং উইডথ 2। কোডটি মনে করে এগুলো সাধারণ এক-ইউনিট অক্ষর। এটি ইনডেক্স অনুযায়ী সেগুলোকে অর্ধেক করে ফেলে। এর ফলে একটি একা সারোগেট (surrogate) রয়ে যায়। এই কারণেই টার্মিনালে একটি ভাঙা বক্স দেখা যায়।

এটি ঠিক করতে আপনাকে অবশ্যই:

  • সারোগেট পেয়ার বাদ দেওয়ার জন্য ফাস্ট পাথটিকে সুরক্ষিত (guard) করতে হবে।
  • কোড ইউনিটের পরিবর্তে কোড পয়েন্ট দিয়ে ট্রিম (trim) করতে হবে।

Array.from(str) ব্যবহার করা সাহায্য করে কারণ এটি কোড পয়েন্ট অনুযায়ী ইটারেট (iterate) করে। এটি নিশ্চিত করে যে আপনি কখনোই একটি অক্ষরকে অর্ধেক করবেন না।

শিক্ষাটি সহজ: কখনোই একটি ইউনিট দিয়ে পরিমাপ করবেন না এবং অন্যটি দিয়ে কাটবেন না। আপনি যদি ডিসপ্লে উইডথ বা কোড পয়েন্ট দিয়ে পরিমাপ করেন, তবে আপনাকে অবশ্যই সেই একই ইউনিট ব্যবহার করে কাটতে হবে।

আপনার কোডটি একটি CJK Extension B ক্যারেক্টার বা একটি ইমোজি দিয়ে পরীক্ষা করুন। ASCII কখনোই এই বাগটি প্রকাশ করবে না।

Source: https://dev.to/greymothjp/a-width-check-said-the-string-was-safe-to-cut-it-split-a-kanji-in-half-4hjk

Optional learning community: https://greymoth-jp.github.io/cjk-failure-corpus/