ഒരു വീതി പരിശോധന (Width Check) ഒരു കാഞ്ചി (Kanji) തകർത്തു
ഒരു പേര് ടെർമിനൽ ടേബിളിലേക്ക് നൽകിയപ്പോൾ അത് തകർന്ന രൂപത്തിൽ പുറത്തുവന്നു. ആ കുടുംബപ്പേര് 𠮷田 എന്നതായിരുന്നു.
ഇതിലെ ആദ്യത്തെ അക്ഷരം സാധാരണയായി ഉപയോഗിക്കുന്ന 吉 എന്നതല്ല. അത് 𠮷 (U+20BB7) ആണ്. യഥാർത്ഥ ജാപ്പനീസ് കുടുംബപ്പേരുകളിൽ ഉപയോഗിക്കുന്ന അപൂർവ്വമായ ഒരു രൂപമാണിത്. ഒരു കോളത്തിൽ ഒതുങ്ങാനായി ടേബിൾ ആ സെല്ലിനെ മുറിച്ചു (truncate). പേരിന് പകരം, തകർന്ന ഒരു അക്ഷരമാണ് അവിടെ പ്രിന്റ് ചെയ്തത്. ആ കാഞ്ചി പകുതിയായി പിളർന്നുപോയി.
ഈ ബഗ് (bug) ഒരു വരി മാത്രമുള്ള ഒരു ഷോർട്ട്കട്ടിലായിരുന്നു (shortcut). സ്ട്രിംഗിനെ യഥാർത്ഥത്തിൽ മുറിക്കുന്നതിന് മുമ്പ്, ഇൻഡക്സ് (index) ഉപയോഗിച്ച് മുറിക്കുന്നത് സുരക്ഷിതമാണോ എന്ന് കോഡ് തീരുമാനിച്ചു. ജാവാസ്ക്രിപ്റ്റ് (JavaScript) സ്ട്രിംഗുകളെ കൈകാര്യം ചെയ്യുന്ന രീതി കാരണം ഈ ലോജിക് പരാജയപ്പെട്ടു.
ഒരു ജാവാസ്ക്രിപ്റ്റ് സ്ട്രിംഗിന് മൂന്ന് വ്യത്യസ്ത നീളങ്ങളുണ്ട്:
- കോഡ് യൂണിറ്റ് നീളം (Code unit length):
"𠮷".lengthഎന്നത് 2 ആണ്. ഇത് UTF-16 യൂണിറ്റുകളെയാണ് കണക്കാക്കുന്നത്. - കോഡ് പോയിന്റ് എണ്ണം (Code point count):
[..."𠮷"].lengthഎന്നത് 1 ആണ്. ഇത് യഥാർത്ഥ അക്ഷരങ്ങളെയാണ് കണക്കാക്കുന്നത്. - ഡിസ്പ്ലേ വീതി (Display width): ഒരു ടെർമിനലിൽ ഇത് എടുക്കുന്ന കോളങ്ങളുടെ എണ്ണം 2 ആണ്.
സാധാരണ ഇംഗ്ലീഷ് ടെക്സ്റ്റുകളിൽ ഈ സംഖ്യകൾ ഒന്നുതന്നെയാണ്. "abc" എന്നതിന് 3 യൂണിറ്റുകളും, 3 പോയിന്റുകളും, 3 കോളങ്ങളും ഉണ്ട്. മിക്ക കോഡുകളും ഈ യാദൃശ്ചികത ഒരു നിയമമാണെന്ന് കരുതുന്നു.
𠮷 എന്ന അക്ഷരം ആ നിയമം ലംഘിക്കുന്നു. ഇതിന് 2 കോഡ് യൂണിറ്റുകളും 2 കോളങ്ങളുമുണ്ട്. സംഖ്യകൾ ഒന്നുതന്നെയാണെങ്കിലും അവ വ്യത്യസ്ത കാരണങ്ങളാലാണ് അങ്ങനെ വരുന്നത്. 2 എന്നത് 2 ആണെന്ന് കണ്ട കോഡ്, ഇൻഡക്സ് ഉപയോഗിച്ച് സ്ട്രിംഗിനെ മുറിക്കുന്നതിനുള്ള ഒരു വേഗത്തിലുള്ള മാർഗ്ഗം (fast path) ഉപയോഗിച്ചു.
ഇൻഡക്സ് 3-ൽ സ്ട്രിംഗ് മുറിച്ചപ്പോൾ, അത് ആദ്യത്തെ പൂർണ്ണമായ അക്ഷരവും രണ്ടാമത്തെ അക്ഷരത്തിന്റെ പകുതിയും മാത്രം എടുത്തു. ഇത് ഒരു ഒറ്റപ്പെട്ട സർഗേറ്റ് (surrogate) അവശേഷിപ്പിച്ചു. ടെർമിനലുകൾ ഇത് ഒരു തകർന്ന ബോക്സ് ആയി കാണിക്കുന്നു.
漢 പോലുള്ള സാധാരണ ജാപ്പനീസ് അക്ഷരങ്ങൾ സുരക്ഷിതമാണ്. അവയ്ക്ക് 1 കോഡ് യൂണിറ്റും 2 കോളങ്ങളുമുണ്ട്. 1 എന്നത് 2-ന് തുല്യമല്ലാത്തതിനാൽ, കോഡ് ആ തകരാറുള്ള ഷോർട്ട്കട്ട് ഒഴിവാക്കുന്നു. അപൂർവ്വമായ അക്ഷരങ്ങളിലും ഇമോജികളിലും (emojis) മാത്രമേ ഈ ബഗ് ബാധിക്കുകയുള്ളൂ.
ഇത് പരിഹരിക്കാൻ, നിങ്ങൾ ചെയ്യേണ്ടത്:
- ഹൈ സർഗേറ്റുകളുള്ള (high surrogates) സ്ട്രിംഗുകളെ നിരസിക്കുന്നതിനായി ഫാസ്റ്റ് പാത്ത് (fast path) സംരക്ഷിക്കുക.
- കോഡ് യൂണിറ്റുകൾക്ക് പകരം പൂർണ്ണമായ കോഡ് പോയിന്റുകൾ ഉപയോഗിച്ച് ട്രിം (trim) ചെയ്യുക.
Array.from(str) ഉപയോഗിക്കുന്നത് ഇത് പരിഹരിക്കും, കാരണം അത് കോഡ് പോയിന്റ് അടിസ്ഥാനത്തിലാണ് പ്രവർത്തിക്കുന്നത്. ഇത് അക്ഷരത്തെ ഒരു പൂർണ്ണ യൂണിറ്റായി പരിഗണിക്കുന്നു.
പാഠം ലളിതമാണ്: ഒരിക്കലും ഒരു യൂണിറ്റ് ഉപയോഗിച്ച് അളക്കുകയും മറ്റൊരു യൂണിറ്റ് ഉപയോഗിച്ച് മുറിക്കുകയും ചെയ്യരുത്. നിങ്ങൾ ഡിസ്പ്ലേ വീതി അളക്കുകയും എന്നാൽ കോഡ് യൂണിറ്റ് ഇൻഡക്സ് ഉപയോഗിച്ച് മുറിക്കുകയും ചെയ്താൽ, ഉപയോക്താക്കളുടെ ഡാറ്റ തകരാറിലാകും.
അപൂർവ്വമായ ഒരു CJK അക്ഷരമോ ഇമോജിയോ ഉപയോഗിച്ച് നിങ്ങളുടെ കോഡ് പരിശോധിക്കുക. ASCII ഉപയോഗിച്ച് ഈ പിശകുകൾ കാണാൻ കഴിയില്ല. നിങ്ങളുടെ കോഡ് ഭയപ്പെടുന്ന ഇൻപുട്ട് തന്നെ നൽകണം.
Optional learning community: https://greymoth-jp.github.io/cjk-failure-corpus/
