İçeriğe geç
academia.sh

Ders 02 / 17

Çıktı Kodlama

Bağlama göre kaçışlamanın kapsamasının ölçülmesi: altı meşru abone notu dört çıktı bağlamına dört ayrı kodlayıcıyla yerleştirilir, on altı eşlemede geri okunamayan yerleştirme sayılır, on dört çıkış noktalı bir envanterde çağrı yerinde kodlayıcı seçen biçim ile bağlamdan türeten biçim karşılaştırılır ve iç içe iki bağlamda kodlayıcı sırasının kaç değeri bozduğu ölçülür.

İçindekiler

Önceki ders giriş noktalarını saydı ve bildirimden türeyen doğrulamanın on beş noktayı kendiliğinden kapattığını gösterdi. Doğrulamayı geçen değer artık sözleşmeye uygundur: abone notu iki yüz karakteri aşmıyor, tarife kümede, dönem biçimli. Bu değerin sistemden çıkarken ne olacağı hakkında doğrulama hiçbir şey söylemez.

Bu ders çıkış noktalarını sayar. Aynı abone notu bir sayfa gövdesine, bir öznitelik tırnağının içine, bir betik değişkenine ve bir adres bileşenine yazılıyor. Dördü de aynı değeri alıyor; dördü de o değeri farklı bir kuralla okuyor. Ölçülen şey bağlamın doğru seçilip seçilmediği ve kaç çıkış noktasının bu seçimi kendi başına yapmak zorunda kaldığıdır.

Dört Çıktı Bağlamı

Bir bağlam üç parçadan oluşur: değeri saran kap, kabın kendi kodlayıcısı ve kabın kendi geri okuyucusu. Ölçü tek sorudur — değer kaba konup kabın kuralıyla geri okunduğunda aynısı mı çıkıyor.

GC5 (varsayım): geri okuma, kabı çözen tarafın kuralını temsil eder. Bir yerleştirme, geri okuma değeri birebir vermediğinde bozulmuş sayılır; bozulmanın ne kadar zararlı olduğu ölçülmez.

// baglam.mjs — dort cikti baglami: kap, kendi kodlayicisi ve kabin kendi geri okuyucusu
// Olcu: deger kaba konup kabin kuraliyla geri okundugunda ayni mi cikiyor?
export const DEGER = ["sayac kapagi kirik", 'abone "acele" notu dusmus', "raf 3 & kat 2",
  "olcum < 5 m3", "ilk satir\nikinci satir", "yuzde 5 artis %"];

const govde = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
export const KODLAYICI = {
  govde,
  oznitelik: (s) => govde(s).replace(/"/g, "&quot;").replace(/\n/g, "&#10;"),
  betik: (s) => JSON.stringify(s).slice(1, -1),
  adres: (s) => encodeURIComponent(s),
};

const coz = (s) => s.replace(/&quot;/g, '"').replace(/&#10;/g, "\n")
  .replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");

export const BAGLAM = {
  govde: [(x) => `<not>${x}</not>`, (k) => {
    const i = k.indexOf("<", 5);
    return k.startsWith("<not>") && k.slice(i) === "</not>" ? coz(k.slice(5, i)) : null;
  }],
  oznitelik: [(x) => `<kayit not="${x}"/>`,
    (k) => { const m = /not="([^"]*)"/.exec(k); return m ? coz(m[1]) : null; }],
  betik: [(x) => `const not = "${x}";`, (k) => {
    try { return JSON.parse(k.slice(k.indexOf('"'), k.lastIndexOf('"') + 1)); } catch { return null; }
  }],
  adres: [(x) => `/rapor?not=${x}&bicim=ozet`, (k) => {
    try {
      const a = k.split("?")[1].split("&").find((p) => p.startsWith("not="));
      return a === undefined ? null : decodeURIComponent(a.slice(4));
    } catch { return null; }
  }],
};

// Tek yerlestirme: deger kodlanir, kaba konur, geri okunur.
export const geriOkunur = (baglam, kodlayiciAdi, deger) => {
  const [kap, oku] = BAGLAM[baglam];
  return oku(kap(KODLAYICI[kodlayiciAdi](deger))) === deger;
};

Altı değerin hepsi meşru abone ve saha notudur: bir tırnak, bir ve imgesi, bir küçüktür imgesi, bir satır sonu, bir yüzde imgesi. Hiçbiri saldırı girdisi değildir; hepsi bir görevlinin yazabileceği metinlerdir.

Bağlam Yanlış Seçildiğinde

Dört bağlam ve dört kodlayıcı on altı eşleme verir. Dördü doğru eşlemedir; kalan on ikisi bağlamın yanlış seçilmesidir.

// secim.mjs — dort baglam x dort kodlayici: baglam yanlis secildiginde kac durum bozuluyor
import { DEGER, KODLAYICI, BAGLAM, geriOkunur } from "./baglam.mjs";

const B = Object.keys(BAGLAM), K = Object.keys(KODLAYICI);
console.log(`${DEGER.length} deger, ${B.length} baglam, ${K.length} kodlayici, ` +
  `${B.length * K.length} esleme, ${DEGER.length * B.length * K.length} yerlestirme\n`);
console.log("bozulan deger sayisi (satir: baglam, sutun: secilen kodlayici)");
console.log("baglam".padEnd(12) + K.map((k) => k.padStart(11)).join(""));
let dogru = 0, yanlis = 0;
for (const b of B) {
  const satir = K.map((k) => DEGER.filter((d) => !geriOkunur(b, k, d)).length);
  K.forEach((k, i) => (k === b ? (dogru += satir[i]) : (yanlis += satir[i])));
  console.log(b.padEnd(12) + satir.map((n) => String(n).padStart(11)).join(""));
}
console.log(`\ndogru baglam secilen ${B.length} eslemede bozulan: ${dogru}`);
console.log(`yanlis baglam secilen ${B.length * K.length - B.length} eslemede bozulan: ${yanlis}`);
6 deger, 4 baglam, 4 kodlayici, 16 esleme, 96 yerlestirme

bozulan deger sayisi (satir: baglam, sutun: secilen kodlayici)
baglam            govde  oznitelik      betik      adres
govde                 0          0          3          6
oznitelik             1          0          2          6
betik                 4          4          0          6
adres                 3          5          4          0

dogru baglam secilen 4 eslemede bozulan: 0
yanlis baglam secilen 12 eslemede bozulan: 44

Köşegen sıfırdır: her bağlam kendi kodlayıcısıyla altı değeri de geri veriyor. Köşegen dışında doksan altı yerleştirmenin kırk dördü bozuluyor.

Son sütun bu tablonun en öğretici yeridir. Adres kodlayıcısı hangi bağlama konursa konsun altı değerin altısını da bozuyor. Eksik kaçışlama değil, fazla kaçışlama bu: boşluklar ve noktalama yüzde imgeli dizilere dönüşüyor ve sayfada okunan not artık yazılan not değil. Yanlış bağlam iki yönde birden hata üretir; sıkı kodlayıcının zararsız olduğu varsayımı ölçümde tutmuyor.

Betik satırı ikinci noktadır. Gövde kodlayıcısı betik bağlamında dört değeri bozuyor, çünkü tırnak ve satır sonu gövde kuralında özel değildir — orada özel olan üç imge başkadır. Kodlayıcının “daha güvenli” olması yetmez; kabın kuralıyla aynı kural olması gerekir.

Çıkış Noktası Envanteri

Bağlam seçimi bir yerde değil, her çıkış noktasında ayrı ayrı yapılır. Bölgesel ölçüm ağının çıkış noktaları sayılıyor: abone portalı, faturalama, saha uygulaması ve rapor üretici.

GC6 (varsayım): her çıkış noktasının gerçek bağlamı bilinir ve envantere doğru yazılmıştır. Ölçülen şey çağrı yerinde seçilen kodlayıcının bu bağlamla uyuşup uyuşmadığıdır.

// cikis-noktalari.mjs — cikis noktasi envanteri (kurgu model) ve iki yazma bicimi
import { DEGER, KODLAYICI, BAGLAM } from "./baglam.mjs";

const KOD = { ...KODLAYICI, ham: (s) => s };
const okunur = (b, k, d) => { const [kap, oku] = BAGLAM[b]; return oku(kap(KOD[k](d))) === d; };

// [servis, cikis noktasi, gercek baglam, cagri yerinde secilen kodlayici]
const CIKIS = [
  ["abone portali", "profil basligi", "govde", "govde"],
  ["abone portali", "not listesi", "govde", "govde"],
  ["abone portali", "form onizleme", "oznitelik", "govde"],
  ["abone portali", "arama baglantisi", "adres", "ham"],
  ["abone portali", "durum betigi", "betik", "govde"],
  ["fatura", "dokum satiri", "govde", "govde"],
  ["fatura", "yazdirma basligi", "oznitelik", "oznitelik"],
  ["fatura", "indirme baglantisi", "adres", "adres"],
  ["saha uygulamasi", "is emri karti", "govde", "govde"],
  ["saha uygulamasi", "harita etiketi", "oznitelik", "ham"],
  ["saha uygulamasi", "sunucu cagrisi", "adres", "adres"],
  ["rapor uretici", "ozet govde", "govde", "govde"],
  ["rapor uretici", "baglanti ureteci", "adres", "govde"],
  ["rapor uretici", "gomulu veri betigi", "betik", "betik"],
];
// Yeni cikis noktalari: cagri yerinde kimse kodlayici secmemis.
const YENI = [["saha uygulamasi", "fotograf basligi", "oznitelik", "ham"],
  ["rapor uretici", "dis baglanti", "adres", "ham"], ["abone portali", "bildirim betigi", "betik", "ham"]];

const olc = (liste, secim) => {
  let bozukNokta = 0, bozukYer = 0;
  for (const [, , baglam, elle] of liste) {
    const n = DEGER.filter((d) => !okunur(baglam, secim === "merkezi" ? baglam : elle, d)).length;
    if (n) bozukNokta++;
    bozukYer += n;
  }
  return [bozukNokta, bozukYer];
};

console.log(`${"asama".padEnd(28)}${"nokta".padStart(6)}${"yerlestirme".padStart(12)}` +
  `${"dagink nokta".padStart(14)}${"dagink yer".padStart(12)}${"merkezi".padStart(9)}`);
for (const [ad, l] of [["mevcut envanter", CIKIS], ["uc yeni nokta eklendi", [...CIKIS, ...YENI]]]) {
  const [dn, dy] = olc(l, "elle"), [, my] = olc(l, "merkezi");
  console.log(ad.padEnd(28) + String(l.length).padStart(6) + String(l.length * DEGER.length).padStart(12) +
    String(dn).padStart(14) + String(dy).padStart(12) + String(my).padStart(9));
}
const say = (b) => CIKIS.filter((c) => c[2] === b).length;
console.log("\nbaglam dagilimi: " + Object.keys(BAGLAM).map((b) => `${b} ${say(b)}`).join("  "));
console.log("cagri yerinde secim: dogru " + CIKIS.filter((c) => c[2] === c[3]).length +
  ", yanlis " + CIKIS.filter((c) => c[3] !== "ham" && c[2] !== c[3]).length +
  ", hic yok " + CIKIS.filter((c) => c[3] === "ham").length);
asama                        nokta yerlestirme  dagink nokta  dagink yer  merkezi
mevcut envanter                 14          84             5          11        0
uc yeni nokta eklendi           17         102             8          16        0

baglam dagilimi: govde 5  oznitelik 3  betik 2  adres 4
cagri yerinde secim: dogru 9, yanlis 3, hic yok 2

On dört çıkış noktasının beşi bozuk yazıyor: üçünde yanlış kodlayıcı seçilmiş, ikisinde hiçbir kodlayıcı yok. Merkezî biçimde bozulma sıfırdır, çünkü kodlayıcı çağrı yerinde seçilmiyor — noktanın bağlam bildiriminden türüyor. Aradaki fark bilgi farkı değil, kararın yeri farkıdır: dokuz çağrı yeri doğru seçmiş, beşi seçmemiş; merkezî biçimde seçilecek bir şey yok.

İkinci satır kapsamanın yeni noktada nasıl davrandığını veriyor. Üç yeni çıkış noktası eklendiğinde dağınık biçimde bozuk nokta beşten sekize çıkıyor — her yeni çıkış noktası doğrudan bir bozulma adayıdır, çünkü kodlayıcıyı yazan biri olmadıkça yoktur. Merkezî biçim aynı üç noktada sıfırda kalıyor.

İç İçe Bağlam

Envanterdeki her nokta tek bir bağlama yazmıyor. Bir öznitelik tırnağının içine bir betik parçası konduğunda değer iki kabın içindedir ve iki kuralla okunur.

// ic-ice.mjs — bir baglamin icine gomulu ikinci baglam: oznitelik icinde betik baglami
import { DEGER, KODLAYICI } from "./baglam.mjs";

const kap = (x) => `<kayit islem="const not = &quot;${x}&quot;;"/>`;
const coz = (s) => s.replace(/&quot;/g, '"').replace(/&#10;/g, "\n")
  .replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
const oku = (k) => {                    // once dis kap, sonra ic kap kendi kuraliyla okunur
  const m = /islem="([^"]*)"/.exec(k);
  if (!m) return null;
  const kod = coz(m[1]);
  try { return JSON.parse(kod.slice(kod.indexOf('"'), kod.lastIndexOf('"') + 1)); } catch { return null; }
};

console.log(`${DEGER.length} deger, ic ice iki baglam: oznitelik icinde betik\n`);
console.log(`${"kodlayici secimi".padEnd(32)}${"geri okunamayan".padStart(16)}`);
for (const [ad, f] of [["yalniz dis baglam (oznitelik)", KODLAYICI.oznitelik],
    ["yalniz ic baglam (betik)", KODLAYICI.betik],
    ["once dis sonra ic", (d) => KODLAYICI.betik(KODLAYICI.oznitelik(d))],
    ["once ic sonra dis", (d) => KODLAYICI.oznitelik(KODLAYICI.betik(d))]])
  console.log(ad.padEnd(32) + String(DEGER.filter((d) => oku(kap(f(d))) !== d).length).padStart(16));
6 deger, ic ice iki baglam: oznitelik icinde betik

kodlayici secimi                 geri okunamayan
yalniz dis baglam (oznitelik)                  2
yalniz ic baglam (betik)                       1
once dis sonra ic                              2
once ic sonra dis                              0

Tek kodlayıcı iki yönde de yetmiyor: dış bağlamınki iki değeri, iç bağlamınki bir değeri bozuyor. İki kodlayıcı birlikte kullanıldığında sonucu sıra belirliyor. Dıştan içe gidilirse iki değer bozuk kalır; içten dışa gidilirse altısı da geri okunur.

Bunun kapsama karşılığı doğrudandır. Çıkış noktası başına tek bir bağlam tutan bir bildirim, iç içe noktayı ifade edemez; o noktalar merkezî kalıbın dışında kalır ve karar yine çağrı yerine döner. Kapsamayı sabit tutmak isteyen bildirim, bağlamı değil bağlam dizisini taşımak zorundadır.

Bağlam dağılımı satırı ayrı bir şey söylüyor: on dört noktanın beşi gövde, dördü adres, üçü öznitelik, ikisi betik bağlamındadır. Dört bağlamın hepsi kullanılıyor ve hiçbiri baskın değil. Tek kodlayıcıyla çalışan bir kod tabanının bu dağılımda doğru sonuç vermesi olanaksızdır; köşegen dışı hücrelerin sayısı doğrudan bu dağılımdan gelir.

Kalıbın Yetmediği İkinci Yer

Merkezî kalıp her noktada doğru kodlayıcıyı seçse bile bir varsayıma dayanır: depodan gelen değer hamdır. Bu varsayım kaldırıldığında kalıp elinde olmayan bir nedenden bozar.

// saklama.mjs — deger depoya kodlanmis girerse merkezi kalip ne yapiyor
import { DEGER, KODLAYICI, BAGLAM } from "./baglam.mjs";

const B = Object.keys(BAGLAM);
const yerlestir = (b, d) => { const [kap, oku] = BAGLAM[b]; return oku(kap(KODLAYICI[b](d))); };
console.log(`${DEGER.length} deger x ${B.length} baglam; merkezi kalip her noktada dogru kodlayiciyi seciyor\n`);
console.log(`${"saklama karari".padEnd(28)}${"bozulan".padStart(9)}  baglam basina`);
for (const [ad, sakla] of [["ham saklanan deger", (d) => d],
    ["giriste bir kez kodlanmis", KODLAYICI.govde]]) {
  const satir = B.map((b) => DEGER.filter((d) => yerlestir(b, sakla(d)) !== d).length);
  console.log(ad.padEnd(28) + String(satir.reduce((a, c) => a + c, 0)).padStart(9) +
    "  " + B.map((b, i) => `${b} ${satir[i]}`).join("  "));
}
6 deger x 4 baglam; merkezi kalip her noktada dogru kodlayiciyi seciyor

saklama karari                bozulan  baglam basina
ham saklanan deger                  0  govde 0  oznitelik 0  betik 0  adres 0
giriste bir kez kodlanmis           8  govde 2  oznitelik 2  betik 2  adres 2

Değer girişte bir kez kodlanıp öyle saklandığında dört bağlamın dördü de ikişer değeri bozuyor. Kodlayıcı seçimi doğrudur, kap doğrudur, geri okuyucu doğrudur; bozan şey değerin depoya girerken uğradığı işlemdir. Ve imgesi ile küçüktür imgesi taşıyan iki not, çıkışta ikinci kez kodlandığı için okuyucuya kaçış dizisiyle varıyor.

Bu bozulmanın kapsama karşılığı ilkinden ağırdır. Çağrı yerindeki yanlış seçim tek noktayı bozar; depodaki yanlış saklama kararı o değeri okuyan her noktayı bozar — bu envanterde on dördünü birden. Kodlamanın yeri çıkıştır ve bunun nedeni ilke değil, sayıdır: çıkışta karar bağlam başına verilir, girişte tek karar bütün bağlamlara dayatılır.

Özet

  • Dört bağlam ve dört kodlayıcı on altı eşleme verdi. Doğru eşlemelerde 96 yerleştirmenin hiçbiri bozulmadı; yanlış bağlam seçilen on iki eşlemede 44 yerleştirme geri okunamadı.
  • Yanlış bağlam iki yönde birden bozar: adres kodlayıcısı diğer üç bağlamda altı değerin altısını da bozdu — eksik kaçışlama değil, fazla kaçışlama. Gövde kodlayıcısı betik bağlamında dört değeri bozdu.
  • On dört çıkış noktasının 9’unda kodlayıcı doğru seçilmiş, 3’ünde yanlış, 2’sinde hiç seçilmemiş; dağınık biçimde 5 nokta ve 11 yerleştirme bozuk. Kodlayıcı bağlam bildiriminden türetildiğinde bozulma 0.
  • Üç yeni çıkış noktası eklendiğinde dağınık biçimde bozuk nokta 5’ten 8’e çıktı, merkezî biçimde 0’da kaldı. Yeni çıkış noktası, kodlayıcısını yazan biri olmadıkça baştan bozuktur.
  • Değer girişte bir kez kodlanıp öyle saklandığında merkezî kalıp doğru çalıştığı hâlde 8 yerleştirme bozuldu. Çağrı yerindeki yanlış seçim tek noktayı, depodaki yanlış saklama kararı o değeri okuyan 14 noktayı birden bozar.
  • İç içe bağlamda tek kodlayıcı yetmedi: dış bağlamınki 2, iç bağlamınki 1 değeri bozdu. Sıra belirleyicidir — içten dışa 0, dıştan içe 2. Nokta başına tek bağlam tutan bir bildirim bu noktaları kapsayamaz.

Sonraki Adım

Çıktı kodlaması değerin okunacağı kabı hedef alır. Değerin sistem içinde bir sorgu metnine konması ayrı bir sorundur: orada kap bir belge değil, bir sorgu dilidir ve o dili okuyan taraf veriyle komutu aynı metinden ayırmak zorundadır. Sonraki ders sorgu noktalarını sayar ve tek soruyu ölçer: kaç sorgu değeri ayrı bir işlenen olarak taşıyan bir arayüzden geçiyor, kaç sorgu değeri metnin kendisine katıyor ve bu iki biçimden hangisi yeni bir sorgu eklendiğinde noktayı kendiliğinden kapsıyor.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat