package main

import (
	"fmt"
	"runtime"
	"strconv"
	"strings"
)

// Transaksi mencatat satu baris penjualan untuk laporan harian.
type Transaksi struct {
	ID    int
	Nama  string
	Total int
}

// dataUji membangun data tetap berdasarkan angka, sehingga kedua fungsi
// diukur dengan input yang sama dan hasilnya dapat dibandingkan.
func dataUji(n int) []Transaksi {
	ts := make([]Transaksi, 0, n)
	for i := 1; i <= n; i++ {
		ts = append(ts, Transaksi{
			ID:    i,
			Nama:  "pesanan-" + strconv.Itoa(i),
			Total: i * 15000,
		})
	}
	return ts
}

// RingkasanSprintf memformat laporan memakai fmt.Sprintf per baris.
// Setiap pemanggilan Sprintf dan setiap penyambungan += mengalokasikan
// string baru di heap.
func RingkasanSprintf(ts []Transaksi) string {
	laporan := ""
	for _, t := range ts {
		laporan += fmt.Sprintf("%d|%s|%d\n", t.ID, t.Nama, t.Total)
	}
	return laporan
}

// RingkasanBuilder memformat laporan yang sama memakai strings.Builder.
// strconv.AppendInt menulis angka ke buffer angka yang dipakai ulang,
// sehingga hampir seluruh baris tidak mengalokasikan memori baru.
func RingkasanBuilder(ts []Transaksi) string {
	var b strings.Builder
	b.Grow(28 * len(ts)) // perkiraan kapasitas dihitung di muka
	var angka [20]byte   // buffer sementara di stack
	for _, t := range ts {
		b.Write(strconv.AppendInt(angka[:0], int64(t.ID), 10))
		b.WriteByte('|')
		b.WriteString(t.Nama)
		b.WriteByte('|')
		b.Write(strconv.AppendInt(angka[:0], int64(t.Total), 10))
		b.WriteByte('\n')
	}
	return b.String()
}

func main() {
	ts := dataUji(200)
	var sebelum, sesudah runtime.MemStats

	// Jendela ukur 1: hanya alokasi RingkasanSprintf yang dihitung.
	runtime.ReadMemStats(&sebelum)
	laporanNaif := RingkasanSprintf(ts)
	runtime.ReadMemStats(&sesudah)
	alokNaif := sesudah.Mallocs - sebelum.Mallocs
	byteNaif := sesudah.TotalAlloc - sebelum.TotalAlloc

	// Jendela ukur 2: hanya alokasi RingkasanBuilder yang dihitung.
	runtime.ReadMemStats(&sebelum)
	laporanBuilder := RingkasanBuilder(ts)
	runtime.ReadMemStats(&sesudah)
	alokBuilder := sesudah.Mallocs - sebelum.Mallocs
	byteBuilder := sesudah.TotalAlloc - sebelum.TotalAlloc

	fmt.Println("Kedua versi menghasilkan laporan identik:", laporanNaif == laporanBuilder)
	fmt.Println("Jumlah baris laporan:", strings.Count(laporanNaif, "\n"))
	fmt.Printf("RingkasanSprintf : %d alokasi, %d byte\n", alokNaif, byteNaif)
	fmt.Printf("RingkasanBuilder : %d alokasi, %d byte\n", alokBuilder, byteBuilder)
}
