lblt 1 maand geleden
bovenliggende
commit
14dae9b6ac
100 gewijzigde bestanden met toevoegingen van 8074 en 4848 verwijderingen
  1. 74 8
      app/init.go
  2. 27 8
      app/server.go
  3. 13 5
      auth/auth.go
  4. 113 34
      auth/jwt.go
  5. 7 7
      dbmanager/database.go
  6. 67 120
      dbmanager/sqlite.go
  7. 89 0
      dbstore/app.go
  8. 58 38
      dbstore/client.go
  9. 360 0
      dbstore/deployment.go
  10. 102 0
      dbstore/template.go
  11. 66 101
      dbstore/user.go
  12. 24 21
      go.mod
  13. 44 39
      go.sum
  14. 138 0
      handlers/apps.go
  15. 32 6
      handlers/auth.go
  16. 0 250
      handlers/autodeploy.go
  17. 8 10
      handlers/clients.go
  18. 96 89
      handlers/deployments.go
  19. 0 129
      handlers/monitoring.go
  20. 87 43
      handlers/templates.go
  21. 11 0
      middleware/auth.go
  22. 105 0
      models/apps.go
  23. 0 49
      models/autodeploy.go
  24. 21 13
      models/client.go
  25. 135 29
      models/deployment.go
  26. 91 13
      models/template.go
  27. 65 11
      models/user.go
  28. 118 0
      services/apps.go
  29. 0 3
      services/autodeploy.go
  30. 430 1
      services/deployments.go
  31. 0 3
      services/monitoring.go
  32. 151 1
      services/templates.go
  33. 5 0
      vendor/github.com/bytedance/sonic/.codespellrc
  34. 4 1
      vendor/github.com/bytedance/sonic/.gitignore
  35. 43 13
      vendor/github.com/bytedance/sonic/README.md
  36. 38 13
      vendor/github.com/bytedance/sonic/README_ZH_CN.md
  37. 35 2
      vendor/github.com/bytedance/sonic/api.go
  38. 3 3
      vendor/github.com/bytedance/sonic/ast/api.go
  39. 4 3
      vendor/github.com/bytedance/sonic/ast/api_compat.go
  40. 75 14
      vendor/github.com/bytedance/sonic/ast/buffer.go
  41. 16 72
      vendor/github.com/bytedance/sonic/ast/decode.go
  42. 35 20
      vendor/github.com/bytedance/sonic/ast/encode.go
  43. 5 1
      vendor/github.com/bytedance/sonic/ast/error.go
  44. 18 5
      vendor/github.com/bytedance/sonic/ast/iterator.go
  45. 172 153
      vendor/github.com/bytedance/sonic/ast/node.go
  46. 124 18
      vendor/github.com/bytedance/sonic/ast/parser.go
  47. 25 6
      vendor/github.com/bytedance/sonic/ast/search.go
  48. 14 14
      vendor/github.com/bytedance/sonic/ast/stubs.go
  49. 0 55
      vendor/github.com/bytedance/sonic/ast/stubs_go115.go
  50. 0 55
      vendor/github.com/bytedance/sonic/ast/stubs_go120.go
  51. 31 14
      vendor/github.com/bytedance/sonic/ast/visitor.go
  52. 3 1
      vendor/github.com/bytedance/sonic/compat.go
  53. 26 19
      vendor/github.com/bytedance/sonic/decoder/decoder_compat.go
  54. 22 18
      vendor/github.com/bytedance/sonic/decoder/decoder_native.go
  55. 4 3
      vendor/github.com/bytedance/sonic/encoder/encoder_compat.go
  56. 4 1
      vendor/github.com/bytedance/sonic/encoder/encoder_native.go
  57. 7 0
      vendor/github.com/bytedance/sonic/go.work.sum
  58. 2 6
      vendor/github.com/bytedance/sonic/internal/caching/hashing.go
  59. 12 0
      vendor/github.com/bytedance/sonic/internal/compat/warn.go
  60. 2 2
      vendor/github.com/bytedance/sonic/internal/cpu/features.go
  61. 29 109
      vendor/github.com/bytedance/sonic/internal/decoder/api/decoder.go
  62. 38 0
      vendor/github.com/bytedance/sonic/internal/decoder/api/decoder_amd64.go
  63. 14 12
      vendor/github.com/bytedance/sonic/internal/decoder/api/decoder_arm64.go
  64. 9 18
      vendor/github.com/bytedance/sonic/internal/decoder/api/stream.go
  65. 0 130
      vendor/github.com/bytedance/sonic/internal/decoder/asm_stubs_amd64_go116.go
  66. 0 1950
      vendor/github.com/bytedance/sonic/internal/decoder/assembler_stkabi_amd64.go
  67. 37 0
      vendor/github.com/bytedance/sonic/internal/decoder/consts/option.go
  68. 8 8
      vendor/github.com/bytedance/sonic/internal/decoder/errors/errors.go
  69. 0 733
      vendor/github.com/bytedance/sonic/internal/decoder/generic_stkabi_amd64.go
  70. 0 0
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/asm.s
  71. 4 9
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/asm_stubs_amd64_go117.go
  72. 5 11
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/asm_stubs_amd64_go121.go
  73. 139 57
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/assembler_regabi_amd64.go
  74. 103 32
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/compiler.go
  75. 1 1
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/debug.go
  76. 141 0
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/decoder.go
  77. 6 5
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/generic_regabi_amd64.go
  78. 1 1
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/generic_regabi_amd64_test.s
  79. 6 4
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/pools.go
  80. 15 1
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/primitives.go
  81. 1 1
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/types.go
  82. 1 1
      vendor/github.com/bytedance/sonic/internal/decoder/jitdec/utils.go
  83. 174 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/compile_struct.go
  84. 460 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/compiler.go
  85. 60 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/const.go
  86. 3 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/context.go
  87. 160 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/decoder.go
  88. 79 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/errors.go
  89. 294 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/functor.go
  90. 110 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/helper.go
  91. 172 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/interface.go
  92. 458 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/map.go
  93. 270 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/native.go
  94. 1298 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/node.go
  95. 235 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/slice.go
  96. 360 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/stringopts.go
  97. 62 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/structs.go
  98. 60 0
      vendor/github.com/bytedance/sonic/internal/decoder/optdec/types.go
  99. 0 111
      vendor/github.com/bytedance/sonic/internal/decoder/stubs_go116.go
  100. 0 111
      vendor/github.com/bytedance/sonic/internal/decoder/stubs_go120.go

+ 74 - 8
app/init.go

@@ -10,6 +10,7 @@ import (
 	"git.linuxforward.com/byop/byop-engine/dbstore"
 	"git.linuxforward.com/byop/byop-engine/handlers"
 	mw "git.linuxforward.com/byop/byop-engine/middleware"
+	"git.linuxforward.com/byop/byop-engine/models"
 	"git.linuxforward.com/byop/byop-engine/services"
 	"github.com/pkg/errors"
 
@@ -45,12 +46,26 @@ func (a *App) initCommonServices() error {
 	// case "mysql":
 	// 	a.dbManager = dbmanager.NewMySQLDbManager(a.cnf.Db.Host, a.cnf.Db.Port, a.cnf.Db.User, a.cnf.Db.Password, a.cnf.Db.Name)
 	default:
-		a.dbManager = dbmanager.NewMemoryDbManager()
+		return fmt.Errorf("unsupported database type: %s", a.cnf.Database.Type)
 	}
 	if err := a.dbManager.Connect(); err != nil {
 		return fmt.Errorf("connect to database: %w", err)
 	}
 
+	// Auto migrate database schema
+	if err := a.dbManager.Migrate(
+		&models.User{},
+		&models.Client{},
+		&models.App{},
+		&models.Template{},
+		&models.Deployment{},
+		&models.DeployedApp{},
+		&models.DeployedAppResource{},
+		// Add other models here
+	); err != nil {
+		return fmt.Errorf("migrate database: %w", err)
+	}
+
 	a.entry.Info("Services initialized successfully, including authentication and database manager")
 	return nil
 }
@@ -59,9 +74,6 @@ func (a *App) initHandlers() error {
 
 	// Initialize UserModule
 	userStore := dbstore.NewUserStore(a.dbManager)
-	if err := userStore.CreateTable(); err != nil {
-		return fmt.Errorf("failed to create users table: %w", err)
-	}
 	userService := services.NewUserService(userStore)
 	userHandler := handlers.NewUserHandler(userService)
 	a.userModule = &UserModule{
@@ -72,9 +84,6 @@ func (a *App) initHandlers() error {
 
 	// Initialize ClientModule
 	clientStore := dbstore.NewClientStore(a.dbManager)
-	if err := clientStore.CreateTable(); err != nil {
-		return fmt.Errorf("failed to create clients table: %w", err)
-	}
 	clientService := services.NewClientService(clientStore)
 	clientHandler := handlers.NewClientHandler(clientService)
 	a.clientModule = &ClientModule{
@@ -83,6 +92,41 @@ func (a *App) initHandlers() error {
 		Handler: clientHandler,
 	}
 
+	// Initialize AppModule
+	appStore := dbstore.NewAppStore(a.dbManager)
+	appService := services.NewAppService(appStore)
+	appHandler := handlers.NewAppHandler(appService)
+	a.appModule = &AppModule{
+		Store:   appStore,
+		Service: appService,
+		Handler: appHandler,
+	}
+
+	// Initialize TemplateModule
+	templateStore := dbstore.NewTemplateStore(a.dbManager)
+	templateService := services.NewTemplateService(templateStore)
+	templateHandler := handlers.NewTemplateHandler(templateService)
+	a.templateModule = &TemplateModule{
+		Store:   templateStore,
+		Service: templateService,
+		Handler: templateHandler,
+	}
+
+	// Initialize DeploymentModule
+	deploymentStore := dbstore.NewDeploymentStore(a.dbManager)
+	deploymentService := services.NewDeploymentService(
+		deploymentStore,
+		appStore,
+		templateStore,
+		clientStore,
+	)
+	deploymentHandler := handlers.NewDeploymentHandler(deploymentService)
+	a.deploymentModule = &DeploymentModule{
+		Store:   deploymentStore,
+		Service: deploymentService,
+		Handler: deploymentHandler,
+	}
+
 	// Initialize authentication handler
 	a.authHandler = handlers.NewAuthHandler(a.authService, a.userModule.Store)
 
@@ -137,6 +181,7 @@ func (a *App) setupRoutes() {
 	providers := protected.Group("/providers")
 	a.providerHandler.RegisterRoutes(providers)
 
+	// Client routes
 	clients := protected.Group("/clients")
 	clients.GET("/", a.clientModule.Handler.ListClients)
 	clients.POST("/", a.clientModule.Handler.CreateClient)
@@ -145,6 +190,7 @@ func (a *App) setupRoutes() {
 	clients.DELETE("/:id", a.clientModule.Handler.DeleteClient)
 	clients.GET("/:id/deployments", a.clientModule.Handler.GetClientDeployments)
 
+	// User routes
 	users := protected.Group("/users")
 	users.GET("/", a.userModule.Handler.ListUsers)
 	users.GET("/:id", a.userModule.Handler.GetUser)
@@ -152,7 +198,27 @@ func (a *App) setupRoutes() {
 	users.DELETE("/:id", a.userModule.Handler.DeleteUser)
 	users.GET("/:id/deployments", a.userModule.Handler.GetUserDeployments)
 
-	// Register other resource routes...
+	// App routes
+	apps := protected.Group("/apps")
+	apps.GET("/", a.appModule.Handler.ListApps)
+	apps.POST("/", a.appModule.Handler.CreateApp)
+	apps.GET("/:id", a.appModule.Handler.GetApp)
+	apps.PUT("/:id", a.appModule.Handler.UpdateApp)
+	apps.DELETE("/:id", a.appModule.Handler.DeleteApp)
+	apps.GET("/:id/deployments", a.appModule.Handler.GetAppDeployments)
+
+	// Template routes
+	templates := protected.Group("/templates")
+	templates.GET("/", a.templateModule.Handler.ListTemplates)
+	templates.POST("/", a.templateModule.Handler.CreateTemplate)
+	templates.GET("/:id", a.templateModule.Handler.GetTemplate)
+	templates.PUT("/:id", a.templateModule.Handler.UpdateTemplate)
+	templates.DELETE("/:id", a.templateModule.Handler.DeleteTemplate)
+	templates.GET("/:id/deployments", a.templateModule.Handler.GetTemplateDeployments)
+
+	// Deployment routes
+	deployments := protected.Group("/deployments")
+	a.deploymentModule.Handler.RegisterRoutes(deployments)
 
 	a.entry.Info("Routes configured successfully")
 }

+ 27 - 8
app/server.go

@@ -36,16 +36,17 @@ type App struct {
 	tokenStore  auth.TokenStore
 
 	// Modules
-	authHandler  *handlers.AuthHandler
-	userModule   *UserModule
-	clientModule *ClientModule
+	authHandler      *handlers.AuthHandler
+	userModule       *UserModule
+	clientModule     *ClientModule
+	appModule        *AppModule
+	templateModule   *TemplateModule
+	deploymentModule *DeploymentModule
 
 	// Resource Handlers
-	providerHandler   *handlers.ProviderHandler
-	deploymentHandler *handlers.DeploymentHandler
-	templateHandler   *handlers.TemplateHandler
-	ticketHandler     *handlers.TicketHandler
-	monitoringHandler *handlers.MonitoringHandler
+	providerHandler *handlers.ProviderHandler
+	// ticketHandler     *handlers.TicketHandler
+	// monitoringHandler *handlers.MonitoringHandler
 }
 
 type UserModule struct {
@@ -60,6 +61,24 @@ type ClientModule struct {
 	Handler *handlers.ClientHandler
 }
 
+type AppModule struct {
+	Store   *dbstore.AppStore
+	Service *services.AppService
+	Handler *handlers.AppHandler
+}
+
+type TemplateModule struct {
+	Store   *dbstore.TemplateStore
+	Service *services.TemplateService
+	Handler *handlers.TemplateHandler
+}
+
+type DeploymentModule struct {
+	Store   *dbstore.DeploymentStore
+	Service *services.DeploymentService
+	Handler *handlers.DeploymentHandler
+}
+
 func NewApp(cnf *config.Config) (*App, error) {
 	ctx, cancelFunc := context.WithCancel(context.Background())
 

+ 13 - 5
auth/auth.go

@@ -5,6 +5,14 @@ import (
 	"errors"
 )
 
+// TokenResponse represents the response containing both access and refresh tokens
+type TokenResponse struct {
+	AccessToken  string `json:"access_token"`
+	RefreshToken string `json:"refresh_token"`
+	ExpiresIn    int64  `json:"expires_in"` // Expiration time in seconds
+	TokenType    string `json:"token_type"` // Usually "Bearer"
+}
+
 var (
 	ErrTokenExpired     = errors.New("token has expired")
 	ErrInvalidToken     = errors.New("token is invalid")
@@ -13,15 +21,15 @@ var (
 
 // Service defines the interface for authentication operations
 type Service interface {
-	// GenerateToken creates a new authentication token for a user
-	GenerateToken(ctx context.Context, clientID string, role string) (string, error)
+	// GenerateToken creates new access and refresh tokens for a user
+	GenerateToken(ctx context.Context, clientID string, role string) (*TokenResponse, error)
 
 	// ValidateToken verifies a token and returns the client ID if valid
 	ValidateToken(ctx context.Context, token string) (string, error)
 
-	// RefreshToken creates a new token based on an existing valid token
-	RefreshToken(ctx context.Context, token string) (string, error)
+	// RefreshToken creates a new access token based on a valid refresh token
+	RefreshToken(ctx context.Context, refreshToken string) (*TokenResponse, error)
 
-	// Logout invalidates a token
+	// Logout invalidates both access and refresh tokens
 	Logout(ctx context.Context, token string) error
 }

+ 113 - 34
auth/jwt.go

@@ -8,18 +8,26 @@ import (
 	"github.com/golang-jwt/jwt"
 )
 
+// Token types
+const (
+	AccessToken  = "access"
+	RefreshToken = "refresh"
+)
+
 // Claims represents the JWT claims structure
 type Claims struct {
 	jwt.StandardClaims
 	ClientID string `json:"client_id"`
 	Role     string `json:"role"`
+	Type     string `json:"type"` // Token type: "access" or "refresh"
 }
 
 // JWTService implements the auth.Service interface using JWT tokens
 type JWTService struct {
-	privateKey    []byte
-	tokenDuration time.Duration
-	tokenStore    TokenStore // Interface for blacklist storage
+	privateKey           []byte
+	tokenDuration        time.Duration
+	refreshTokenDuration time.Duration // Duration for refresh tokens (typically longer)
+	tokenStore           TokenStore    // Interface for blacklist storage
 }
 
 // TokenStore defines storage operations for token management
@@ -31,31 +39,68 @@ type TokenStore interface {
 // NewJWTService creates a new JWT-based auth service
 func NewJWTService(privateKey []byte, tokenDuration time.Duration, store TokenStore) *JWTService {
 	return &JWTService{
-		privateKey:    privateKey,
-		tokenDuration: tokenDuration,
-		tokenStore:    store,
+		privateKey:           privateKey,
+		tokenDuration:        tokenDuration,
+		refreshTokenDuration: tokenDuration * 24, // Refresh tokens valid for 24x longer than access tokens
+		tokenStore:           store,
+	}
+}
+
+// generateTokenWithClaims generates a JWT token with the given claims
+func (s *JWTService) generateTokenWithClaims(claims *Claims) (string, error) {
+	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+	signedToken, err := token.SignedString(s.privateKey)
+	if err != nil {
+		return "", err
 	}
+	return signedToken, nil
 }
 
-// GenerateToken generates a new JWT token for a client
-func (s *JWTService) GenerateToken(ctx context.Context, clientID string, role string) (string, error) {
-	expirationTime := time.Now().Add(s.tokenDuration)
+// GenerateToken generates new access and refresh tokens for a client
+func (s *JWTService) GenerateToken(ctx context.Context, clientID string, role string) (*TokenResponse, error) {
+	now := time.Now()
+	accessExpiration := now.Add(s.tokenDuration)
+	refreshExpiration := now.Add(s.refreshTokenDuration)
 
-	claims := &Claims{
+	// Create access token claims
+	accessClaims := &Claims{
 		ClientID: clientID,
 		Role:     role,
+		Type:     AccessToken,
 		StandardClaims: jwt.StandardClaims{
-			ExpiresAt: expirationTime.Unix(),
+			ExpiresAt: accessExpiration.Unix(),
+			IssuedAt:  now.Unix(),
 		},
 	}
 
-	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
-	signedToken, err := token.SignedString(s.privateKey)
+	// Create refresh token claims
+	refreshClaims := &Claims{
+		ClientID: clientID,
+		Role:     role,
+		Type:     RefreshToken,
+		StandardClaims: jwt.StandardClaims{
+			ExpiresAt: refreshExpiration.Unix(),
+			IssuedAt:  now.Unix(),
+		},
+	}
+
+	// Generate the tokens
+	accessToken, err := s.generateTokenWithClaims(accessClaims)
 	if err != nil {
-		return "", err
+		return nil, err
 	}
 
-	return signedToken, nil
+	refreshToken, err := s.generateTokenWithClaims(refreshClaims)
+	if err != nil {
+		return nil, err
+	}
+
+	return &TokenResponse{
+		AccessToken:  accessToken,
+		RefreshToken: refreshToken,
+		ExpiresIn:    int64(s.tokenDuration.Seconds()),
+		TokenType:    "Bearer",
+	}, nil
 }
 
 // ValidateToken validates a JWT token and returns the client ID if valid
@@ -66,56 +111,90 @@ func (s *JWTService) ValidateToken(ctx context.Context, tokenString string) (str
 		return "", err
 	}
 	if isBlacklisted {
-		return "", errors.New("token is blacklisted")
+		return "", ErrTokenBlacklisted
 	}
 
 	// Parse and validate the token
+	claims, err := s.parseToken(tokenString)
+	if err != nil {
+		return "", err
+	}
+
+	// For validation purposes, we only accept access tokens
+	if claims.Type != AccessToken {
+		return "", errors.New("invalid token type")
+	}
+
+	return claims.ClientID, nil
+}
+
+// parseToken parses and validates a JWT token and returns the claims if valid
+func (s *JWTService) parseToken(tokenString string) (*Claims, error) {
 	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
 		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
 			return nil, errors.New("unexpected signing method")
 		}
 		return s.privateKey, nil
 	})
+
 	if err != nil {
-		return "", err
+		if ve, ok := err.(*jwt.ValidationError); ok {
+			if ve.Errors&jwt.ValidationErrorExpired != 0 {
+				return nil, ErrTokenExpired
+			}
+		}
+		return nil, ErrInvalidToken
 	}
 
 	// Extract claims
 	claims, ok := token.Claims.(*Claims)
 	if !ok || !token.Valid {
-		return "", errors.New("invalid token")
+		return nil, ErrInvalidToken
 	}
 
-	return claims.ClientID, nil
+	return claims, nil
 }
 
-// RefreshToken refreshes an existing token and returns a new one
-func (s *JWTService) RefreshToken(ctx context.Context, tokenString string) (string, error) {
-	// Validate the existing token
-	clientID, err := s.ValidateToken(ctx, tokenString)
+// RefreshToken validates a refresh token and generates new access and refresh tokens
+func (s *JWTService) RefreshToken(ctx context.Context, refreshTokenString string) (*TokenResponse, error) {
+	// Check if the refresh token is blacklisted
+	isBlacklisted, err := s.tokenStore.IsBlacklisted(ctx, refreshTokenString)
 	if err != nil {
-		return "", err
+		return nil, err
+	}
+	if isBlacklisted {
+		return nil, ErrTokenBlacklisted
 	}
 
-	// Generate a new token
-	return s.GenerateToken(ctx, clientID, "")
+	// Parse and validate the token
+	claims, err := s.parseToken(refreshTokenString)
+	if err != nil {
+		return nil, err
+	}
+
+	// Check if it's a refresh token
+	if claims.Type != RefreshToken {
+		return nil, errors.New("not a refresh token")
+	}
+
+	// Blacklist the old refresh token to prevent reuse
+	expiry := time.Unix(claims.ExpiresAt, 0)
+	if err := s.tokenStore.Blacklist(ctx, refreshTokenString, expiry); err != nil {
+		return nil, err
+	}
+
+	// Generate new access and refresh tokens
+	return s.GenerateToken(ctx, claims.ClientID, claims.Role)
 }
 
 // Logout invalidates a token by blacklisting it
 func (s *JWTService) Logout(ctx context.Context, tokenString string) error {
 	// Parse the token to extract expiration time
-	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
-		return s.privateKey, nil
-	})
+	claims, err := s.parseToken(tokenString)
 	if err != nil {
 		return err
 	}
 
-	claims, ok := token.Claims.(*Claims)
-	if !ok || !token.Valid {
-		return errors.New("invalid token")
-	}
-
 	expiry := time.Unix(claims.ExpiresAt, 0)
 	return s.tokenStore.Blacklist(ctx, tokenString, expiry)
 }

+ 7 - 7
dbmanager/database.go

@@ -1,13 +1,13 @@
 package dbmanager
 
-// DbManager defines the interface for database operations
+import (
+	"gorm.io/gorm"
+)
+
+// DbManager is the interface for database operations
 type DbManager interface {
+	GetDB() *gorm.DB
 	Connect() error
 	Disconnect() error
-	Create(entityType string, entity interface{}) error
-	GetByID(entityType string, id string) (interface{}, error)
-	Update(entityType string, entity interface{}) error
-	Delete(entityType string, id string) error
-	List(entityType string, filter map[string]interface{}) ([]interface{}, error)
-	Exec(query string, args ...interface{}) (interface{}, error)
+	Migrate(models ...interface{}) error
 }

+ 67 - 120
dbmanager/sqlite.go

@@ -1,158 +1,105 @@
 package dbmanager
 
 import (
-	"database/sql"
-	"encoding/json"
 	"fmt"
+	"os"
 
-	"github.com/google/uuid"
-	_ "github.com/mattn/go-sqlite3" // SQLite driver
+	"gorm.io/driver/sqlite"
+	"gorm.io/gorm"
+	"gorm.io/gorm/logger"
 )
 
+// SQLiteManager implements the DbManager interface for SQLite using GORM
 type SQLiteManager struct {
-	db *sql.DB
+	db  *gorm.DB
+	dsn string
 }
 
 // NewSQLiteManager initializes a new SQLiteManager
 func NewSQLiteManager(dataSourceName string) (*SQLiteManager, error) {
-	db, err := sql.Open("sqlite3", dataSourceName)
-	if err != nil {
-		return nil, fmt.Errorf("failed to connect to SQLite database: %w", err)
+	// First check if the database file exists
+	isNewDb := !fileExists(dataSourceName)
+	if isNewDb {
+		// Create the database file if it doesn't exist
+		file, err := os.Create(dataSourceName)
+		if err != nil {
+			return nil, fmt.Errorf("failed to create SQLite database file: %w", err)
+		}
+		defer file.Close()
 	}
 
-	// Enable foreign key constraints
-	_, err = db.Exec("PRAGMA foreign_keys = ON;")
+	// Open SQLite database with GORM and SQLite-specific configuration
+	db, err := gorm.Open(sqlite.Open(dataSourceName), &gorm.Config{
+		Logger: logger.Default.LogMode(logger.Silent),
+		// Set DisableForeignKeyConstraintWhenMigrating to true to avoid foreign key issues during migration
+		DisableForeignKeyConstraintWhenMigrating: true,
+	})
 	if err != nil {
-		return nil, fmt.Errorf("failed to enable foreign keys: %w", err)
+		return nil, fmt.Errorf("failed to connect to SQLite database: %w", err)
 	}
 
-	return &SQLiteManager{db: db}, nil
-}
+	// Enable foreign keys in SQLite after migrations
+	db.Exec("PRAGMA foreign_keys = ON")
 
-// Connect establishes a connection to the SQLite database
-func (m *SQLiteManager) Connect() error {
-	// No action needed for SQLite
-	return nil
-}
-
-// Disconnect closes the connection to the SQLite database
-func (m *SQLiteManager) Disconnect() error {
-	return m.db.Close()
+	return &SQLiteManager{
+		db:  db,
+		dsn: dataSourceName,
+	}, nil
 }
 
-// Create inserts a new record into the specified table
-func (m *SQLiteManager) Create(table string, data interface{}) error {
-	jsonData, err := json.Marshal(data)
-	if err != nil {
-		return fmt.Errorf("failed to marshal data: %w", err)
-	}
-
-	id := uuid.New().String()
-
-	query := fmt.Sprintf("INSERT INTO %s (id, data) VALUES (?, ?)", table)
-	_, err = m.db.Exec(query, id, jsonData)
-	if err != nil {
-		return fmt.Errorf("failed to insert data into table %s: %w", table, err)
-	}
-	return nil
+// fileExists checks if a file exists
+func fileExists(filename string) bool {
+	_, err := os.Stat(filename)
+	return !os.IsNotExist(err)
 }
 
-// GetByID retrieves a record by ID from the specified table
-func (m *SQLiteManager) GetByID(table string, id string) (interface{}, error) {
-	query := fmt.Sprintf("SELECT data FROM %s WHERE id = ?", table)
-	row := m.db.QueryRow(query, id)
-
-	var jsonData string
-	if err := row.Scan(&jsonData); err != nil {
-		if err == sql.ErrNoRows {
-			return nil, nil // No record found
-		}
-		return nil, fmt.Errorf("failed to retrieve data from table %s: %w", table, err)
-	}
-
-	var result map[string]interface{}
-	if err := json.Unmarshal([]byte(jsonData), &result); err != nil {
-		return nil, fmt.Errorf("failed to unmarshal data: %w", err)
-	}
-
-	return result, nil
+// GetDB returns the GORM database instance
+func (m *SQLiteManager) GetDB() *gorm.DB {
+	return m.db
 }
 
-// Update updates an existing record in the specified table
-func (m *SQLiteManager) Update(table string, data interface{}) error {
-	jsonData, err := json.Marshal(data)
-	if err != nil {
-		return fmt.Errorf("failed to marshal data: %w", err)
-	}
-
-	// Assuming the data contains an "id" field
-	id := data.(map[string]interface{})["id"]
-	query := fmt.Sprintf("UPDATE %s SET data = ? WHERE id = ?", table)
-	_, err = m.db.Exec(query, jsonData, id)
-	if err != nil {
-		return fmt.Errorf("failed to update data in table %s: %w", table, err)
-	}
-
+// Connect establishes a connection to the SQLite database
+func (m *SQLiteManager) Connect() error {
+	// Connection is already established in NewSQLiteManager
 	return nil
 }
 
-// Delete removes a record by ID from the specified table
-func (m *SQLiteManager) Delete(table string, id string) error {
-	query := fmt.Sprintf("DELETE FROM %s WHERE id = ?", table)
-	_, err := m.db.Exec(query, id)
+// Disconnect closes the connection to the SQLite database
+func (m *SQLiteManager) Disconnect() error {
+	sqlDB, err := m.db.DB()
 	if err != nil {
-		return fmt.Errorf("failed to delete data from table %s: %w", table, err)
+		return err
 	}
-
-	return nil
+	return sqlDB.Close()
 }
 
-// List retrieves all records from the specified table
-func (m *SQLiteManager) List(table string, filter map[string]interface{}) ([]interface{}, error) {
-	query := fmt.Sprintf("SELECT data FROM %s", table)
-	rows, err := m.db.Query(query)
-	if err != nil {
-		return nil, fmt.Errorf("failed to list data from table %s: %w", table, err)
-	}
-	defer rows.Close()
-
-	var results []interface{}
-	for rows.Next() {
-		var jsonData string
-		if err := rows.Scan(&jsonData); err != nil {
-			return nil, fmt.Errorf("failed to scan row: %w", err)
-		}
-
-		var result map[string]interface{}
-		if err := json.Unmarshal([]byte(jsonData), &result); err != nil {
-			return nil, fmt.Errorf("failed to unmarshal data: %w", err)
-		}
+// Migrate runs auto migration for the provided models
+func (m *SQLiteManager) Migrate(models ...interface{}) error {
+	// Check if database file exists before
+	isNewDb := !fileExists(m.dsn)
 
-		results = append(results, result)
+	// SQLite has limitations with ALTER TABLE for adding NOT NULL columns
+	// For a new database, we can just create tables directly
+	if isNewDb {
+		return m.db.AutoMigrate(models...)
 	}
 
-	return results, nil
-}
-
-// Exec executes a raw SQL query
-func (m *SQLiteManager) Exec(query string, args ...interface{}) (interface{}, error) {
-	result, err := m.db.Exec(query, args...)
-	if err != nil {
-		return nil, fmt.Errorf("failed to execute query: %w", err)
+	// For existing database, we need a more careful approach to avoid NOT NULL errors
+	// Try to create tables that don't exist
+	migrator := m.db.Migrator()
+
+	for _, model := range models {
+		// Check if the table exists
+		if !migrator.HasTable(model) {
+			// If table doesn't exist, create it
+			if err := migrator.CreateTable(model); err != nil {
+				return fmt.Errorf("failed to create table for %T: %w", model, err)
+			}
+		}
 	}
-	return result, nil
-}
 
-// Query executes a raw SQL query and returns the rows
-func (m *SQLiteManager) Query(query string, args ...interface{}) (*sql.Rows, error) {
-	rows, err := m.db.Query(query, args...)
-	if err != nil {
-		return nil, fmt.Errorf("failed to execute query: %w", err)
-	}
-	return rows, nil
-}
+	// Re-enable foreign key constraints
+	m.db.Exec("PRAGMA foreign_keys = ON")
 
-// Close closes the database connection
-func (m *SQLiteManager) Close() error {
-	return m.db.Close()
+	return nil
 }

+ 89 - 0
dbstore/app.go

@@ -0,0 +1,89 @@
+package dbstore
+
+import (
+	"fmt"
+
+	"git.linuxforward.com/byop/byop-engine/dbmanager"
+	"git.linuxforward.com/byop/byop-engine/models"
+	"github.com/google/uuid"
+	"gorm.io/gorm"
+)
+
+// AppStore handles database operations for apps
+type AppStore struct {
+	db *gorm.DB
+}
+
+// NewAppStore creates a new AppStore
+func NewAppStore(dbManager dbmanager.DbManager) *AppStore {
+	return &AppStore{
+		db: dbManager.GetDB(),
+	}
+}
+
+// Create creates a new app
+func (as *AppStore) Create(app *models.App) error {
+	// Generate ID if not provided
+	if app.ID == "" {
+		app.ID = uuid.New().String()
+	}
+
+	// GORM will handle created_at and updated_at automatically
+	return as.db.Create(app).Error
+}
+
+// GetByID retrieves an app by ID
+func (as *AppStore) GetByID(id string) (*models.App, error) {
+	var app models.App
+	result := as.db.First(&app, "id = ?", id)
+	if result.Error != nil {
+		if result.Error == gorm.ErrRecordNotFound {
+			return nil, nil // No app found
+		}
+		return nil, fmt.Errorf("failed to get app: %w", result.Error)
+	}
+	return &app, nil
+}
+
+// Update updates an existing app
+func (as *AppStore) Update(app *models.App) error {
+	return as.db.Save(app).Error
+}
+
+// Delete deletes an app by ID
+func (as *AppStore) Delete(id string) error {
+	return as.db.Delete(&models.App{}, "id = ?", id).Error
+}
+
+// List retrieves all apps with optional filtering
+func (as *AppStore) List(filter map[string]interface{}) ([]*models.App, error) {
+	var apps []*models.App
+
+	// Build query from filters
+	query := as.db
+	if filter != nil {
+		for key, value := range filter {
+			query = query.Where(key+" = ?", value)
+		}
+	}
+
+	// Execute query
+	if err := query.Find(&apps).Error; err != nil {
+		return nil, fmt.Errorf("failed to list apps: %w", err)
+	}
+
+	return apps, nil
+}
+
+// GetAppWithDeployments retrieves an app by ID with associated deployments
+func (as *AppStore) GetAppWithDeployments(id string) (*models.App, error) {
+	var app models.App
+	result := as.db.Preload("Deployments").First(&app, "id = ?", id)
+	if result.Error != nil {
+		if result.Error == gorm.ErrRecordNotFound {
+			return nil, nil // No app found
+		}
+		return nil, fmt.Errorf("failed to get app: %w", result.Error)
+	}
+	return &app, nil
+}

+ 58 - 38
dbstore/client.go

@@ -5,65 +5,85 @@ import (
 
 	"git.linuxforward.com/byop/byop-engine/dbmanager"
 	"git.linuxforward.com/byop/byop-engine/models"
+	"github.com/google/uuid"
+	"gorm.io/gorm"
 )
 
+// ClientStore handles database operations for clients
 type ClientStore struct {
-	dbConn dbmanager.DbManager
+	db *gorm.DB
 }
 
 // NewClientStore creates a new ClientStore
-func NewClientStore(dbConn dbmanager.DbManager) *ClientStore {
+func NewClientStore(dbManager dbmanager.DbManager) *ClientStore {
 	return &ClientStore{
-		dbConn: dbConn,
+		db: dbManager.GetDB(),
 	}
 }
-func (cs *ClientStore) CreateTable() error {
-	query := `
-    CREATE TABLE IF NOT EXISTS clients (
-        id TEXT PRIMARY KEY,
-        data TEXT NOT NULL
-    );`
-	_, err := cs.dbConn.Exec(query)
-	if err != nil {
-		return fmt.Errorf("failed to create clients table: %w", err)
+
+// Create creates a new client
+func (cs *ClientStore) Create(client *models.Client) error {
+	// Generate ID if not provided
+	if client.ID == "" {
+		client.ID = uuid.New().String()
 	}
-	return nil
-}
 
-// Create creates a new user
-func (us *ClientStore) Create(user *models.Client) error {
-	return us.dbConn.Create("clients", user)
+	// GORM will handle created_at and updated_at automatically
+	return cs.db.Create(client).Error
 }
 
-// GetByID retrieves a user by ID
-func (us *ClientStore) GetByID(id string) (*models.Client, error) {
-	user, err := us.dbConn.GetByID("clients", id)
-	if err != nil {
-		return nil, err
+// GetByID retrieves a client by ID
+func (cs *ClientStore) GetByID(id string) (*models.Client, error) {
+	var client models.Client
+	result := cs.db.First(&client, "id = ?", id)
+	if result.Error != nil {
+		if result.Error == gorm.ErrRecordNotFound {
+			return nil, nil // No client found
+		}
+		return nil, fmt.Errorf("failed to get client: %w", result.Error)
 	}
-	return user.(*models.Client), nil
+	return &client, nil
 }
 
-// Update updates an existing user
-func (us *ClientStore) Update(user *models.Client) error {
-	return us.dbConn.Update("clients", user)
+// Update updates an existing client
+func (cs *ClientStore) Update(client *models.Client) error {
+	return cs.db.Save(client).Error
 }
 
-// Delete deletes a user by ID
-func (us *ClientStore) Delete(id string) error {
-	return us.dbConn.Delete("clients", id)
+// Delete deletes a client by ID
+func (cs *ClientStore) Delete(id string) error {
+	return cs.db.Delete(&models.Client{}, "id = ?", id).Error
 }
 
-// List retrieves all clients
-func (us *ClientStore) List(filter map[string]interface{}) ([]*models.Client, error) {
-	clients, err := us.dbConn.List("clients", filter)
-	if err != nil {
-		return nil, err
+// List retrieves all clients with optional filtering
+func (cs *ClientStore) List(filter map[string]interface{}) ([]*models.Client, error) {
+	var clients []*models.Client
+
+	// Build query from filters
+	query := cs.db
+	if filter != nil {
+		for key, value := range filter {
+			query = query.Where(key+" = ?", value)
+		}
 	}
 
-	var userList []*models.Client
-	for _, user := range clients {
-		userList = append(userList, user.(*models.Client))
+	// Execute query
+	if err := query.Find(&clients).Error; err != nil {
+		return nil, fmt.Errorf("failed to list clients: %w", err)
+	}
+
+	return clients, nil
+}
+
+// GetClientWithDeployments retrieves a client by ID with associated deployments
+func (cs *ClientStore) GetClientWithDeployments(id string) (*models.Client, error) {
+	var client models.Client
+	result := cs.db.Preload("Deployments").First(&client, "id = ?", id)
+	if result.Error != nil {
+		if result.Error == gorm.ErrRecordNotFound {
+			return nil, nil // No client found
+		}
+		return nil, fmt.Errorf("failed to get client: %w", result.Error)
 	}
-	return userList, nil
+	return &client, nil
 }

+ 360 - 0
dbstore/deployment.go

@@ -0,0 +1,360 @@
+package dbstore
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"git.linuxforward.com/byop/byop-engine/dbmanager"
+	"git.linuxforward.com/byop/byop-engine/models"
+	"github.com/google/uuid"
+	"gorm.io/gorm"
+)
+
+// DeploymentStore handles database operations for deployments
+type DeploymentStore struct {
+	db *gorm.DB
+}
+
+// NewDeploymentStore creates a new DeploymentStore
+func NewDeploymentStore(dbManager dbmanager.DbManager) *DeploymentStore {
+	return &DeploymentStore{
+		db: dbManager.GetDB(),
+	}
+}
+
+// Create creates a new deployment
+func (ds *DeploymentStore) Create(deployment *models.Deployment) error {
+	// Generate ID if not provided
+	if deployment.ID == "" {
+		deployment.ID = uuid.New().String()
+	}
+
+	// Ensure logs, metrics, and alerts config are properly JSON serialized
+	if err := ds.serializeConfigFields(deployment); err != nil {
+		return fmt.Errorf("failed to serialize config fields: %w", err)
+	}
+
+	// Create deployment in a transaction to handle deployed apps
+	return ds.db.Transaction(func(tx *gorm.DB) error {
+		// Create the deployment
+		if err := tx.Create(deployment).Error; err != nil {
+			return err
+		}
+
+		// Create any deployed apps in the same transaction
+		for i := range deployment.DeployedApps {
+			app := &deployment.DeployedApps[i]
+
+			// Ensure each deployed app has an ID
+			if app.ID == "" {
+				app.ID = uuid.New().String()
+			}
+
+			// Set the deployment ID (ensure relationship is maintained)
+			app.DeploymentID = deployment.ID
+
+			// Create the deployed app
+			if err := tx.Create(app).Error; err != nil {
+				return err
+			}
+
+			// Handle resources if provided
+			if app.Resources != (models.ResourceAllocation{}) {
+				resource := models.DeployedAppResource{
+					ID:            uuid.New().String(),
+					DeployedAppID: app.ID,
+					CPU:           app.Resources.CPU,
+					CPUUsage:      app.Resources.CPUUsage,
+					Memory:        app.Resources.Memory,
+					MemoryUsage:   app.Resources.MemoryUsage,
+					Storage:       app.Resources.Storage,
+					StorageUsage:  app.Resources.StorageUsage,
+				}
+
+				if err := tx.Create(&resource).Error; err != nil {
+					return err
+				}
+			}
+		}
+
+		return nil
+	})
+}
+
+// GetByID retrieves a deployment by ID
+func (ds *DeploymentStore) GetByID(id string) (*models.Deployment, error) {
+	var deployment models.Deployment
+
+	// Get deployment with all related deployed apps
+	err := ds.db.
+		Preload("DeployedApps").
+		First(&deployment, "id = ?", id).Error
+
+	if err != nil {
+		if err == gorm.ErrRecordNotFound {
+			return nil, nil // No deployment found
+		}
+		return nil, fmt.Errorf("failed to get deployment: %w", err)
+	}
+
+	// Load resources for each deployed app
+	for i, app := range deployment.DeployedApps {
+		var resource models.DeployedAppResource
+		if err := ds.db.Where("deployed_app_id = ?", app.ID).First(&resource).Error; err != nil {
+			if err != gorm.ErrRecordNotFound {
+				return nil, fmt.Errorf("failed to get resources for deployed app: %w", err)
+			}
+		} else {
+			deployment.DeployedApps[i].Resources = models.ResourceAllocation{
+				CPU:          resource.CPU,
+				CPUUsage:     resource.CPUUsage,
+				Memory:       resource.Memory,
+				MemoryUsage:  resource.MemoryUsage,
+				Storage:      resource.Storage,
+				StorageUsage: resource.StorageUsage,
+			}
+		}
+	}
+
+	// Deserialize config fields
+	if err := ds.deserializeConfigFields(&deployment); err != nil {
+		return nil, fmt.Errorf("failed to deserialize config fields: %w", err)
+	}
+
+	return &deployment, nil
+}
+
+// Update updates an existing deployment
+func (ds *DeploymentStore) Update(deployment *models.Deployment) error {
+	// Ensure logs, metrics, and alerts config are properly JSON serialized
+	if err := ds.serializeConfigFields(deployment); err != nil {
+		return fmt.Errorf("failed to serialize config fields: %w", err)
+	}
+
+	// Use transaction to handle deployment and deployed apps
+	return ds.db.Transaction(func(tx *gorm.DB) error {
+		// Update the deployment
+		if err := tx.Save(deployment).Error; err != nil {
+			return err
+		}
+
+		// Handle deployed apps - this is trickier as we need to compare with existing apps
+		var existingApps []models.DeployedApp
+		if err := tx.Where("deployment_id = ?", deployment.ID).Find(&existingApps).Error; err != nil {
+			return err
+		}
+
+		// Create a map of existing app IDs for quick lookup
+		existingAppMap := make(map[string]bool)
+		for _, app := range existingApps {
+			existingAppMap[app.ID] = true
+		}
+
+		// Process each app in the updated deployment
+		for i := range deployment.DeployedApps {
+			app := &deployment.DeployedApps[i]
+
+			// If app has ID and exists, update it
+			if app.ID != "" && existingAppMap[app.ID] {
+				if err := tx.Save(app).Error; err != nil {
+					return err
+				}
+				delete(existingAppMap, app.ID)
+			} else {
+				// New app, create it
+				if app.ID == "" {
+					app.ID = uuid.New().String()
+				}
+				app.DeploymentID = deployment.ID
+				if err := tx.Create(app).Error; err != nil {
+					return err
+				}
+			}
+
+			// Handle resources
+			if app.Resources != (models.ResourceAllocation{}) {
+				var resource models.DeployedAppResource
+				result := tx.Where("deployed_app_id = ?", app.ID).First(&resource)
+				if result.Error != nil && result.Error != gorm.ErrRecordNotFound {
+					return result.Error
+				}
+
+				if result.Error == gorm.ErrRecordNotFound {
+					// Create new resource
+					resource = models.DeployedAppResource{
+						ID:            uuid.New().String(),
+						DeployedAppID: app.ID,
+						CPU:           app.Resources.CPU,
+						CPUUsage:      app.Resources.CPUUsage,
+						Memory:        app.Resources.Memory,
+						MemoryUsage:   app.Resources.MemoryUsage,
+						Storage:       app.Resources.Storage,
+						StorageUsage:  app.Resources.StorageUsage,
+					}
+					if err := tx.Create(&resource).Error; err != nil {
+						return err
+					}
+				} else {
+					// Update existing resource
+					resource.CPU = app.Resources.CPU
+					resource.CPUUsage = app.Resources.CPUUsage
+					resource.Memory = app.Resources.Memory
+					resource.MemoryUsage = app.Resources.MemoryUsage
+					resource.Storage = app.Resources.Storage
+					resource.StorageUsage = app.Resources.StorageUsage
+					if err := tx.Save(&resource).Error; err != nil {
+						return err
+					}
+				}
+			}
+		}
+
+		// Delete any apps that are no longer part of the deployment
+		for appID := range existingAppMap {
+			if err := tx.Delete(&models.DeployedApp{}, "id = ?", appID).Error; err != nil {
+				return err
+			}
+			// Delete associated resources
+			if err := tx.Delete(&models.DeployedAppResource{}, "deployed_app_id = ?", appID).Error; err != nil && err != gorm.ErrRecordNotFound {
+				return err
+			}
+		}
+
+		return nil
+	})
+}
+
+// Delete deletes a deployment by ID
+func (ds *DeploymentStore) Delete(id string) error {
+	return ds.db.Transaction(func(tx *gorm.DB) error {
+		// Delete associated DeployedAppResources
+		var deployedApps []models.DeployedApp
+		if err := tx.Where("deployment_id = ?", id).Find(&deployedApps).Error; err != nil {
+			return err
+		}
+
+		for _, app := range deployedApps {
+			if err := tx.Delete(&models.DeployedAppResource{}, "deployed_app_id = ?", app.ID).Error; err != nil && err != gorm.ErrRecordNotFound {
+				return err
+			}
+		}
+
+		// Delete deployed apps
+		if err := tx.Delete(&models.DeployedApp{}, "deployment_id = ?", id).Error; err != nil && err != gorm.ErrRecordNotFound {
+			return err
+		}
+
+		// Delete the deployment itself
+		return tx.Delete(&models.Deployment{}, "id = ?", id).Error
+	})
+}
+
+// List retrieves all deployments with optional filtering
+func (ds *DeploymentStore) List(filter map[string]interface{}) ([]*models.Deployment, error) {
+	var deployments []*models.Deployment
+
+	// Build query from filters
+	query := ds.db.Preload("DeployedApps")
+	if filter != nil {
+		for key, value := range filter {
+			query = query.Where(key+" = ?", value)
+		}
+	}
+
+	// Execute query
+	if err := query.Find(&deployments).Error; err != nil {
+		return nil, fmt.Errorf("failed to list deployments: %w", err)
+	}
+
+	// Load resources and deserialize config for each deployment
+	for i, deployment := range deployments {
+		// Load resources for each deployed app
+		for j, app := range deployment.DeployedApps {
+			var resource models.DeployedAppResource
+			if err := ds.db.Where("deployed_app_id = ?", app.ID).First(&resource).Error; err != nil {
+				if err != gorm.ErrRecordNotFound {
+					return nil, fmt.Errorf("failed to get resources for deployed app: %w", err)
+				}
+			} else {
+				deployments[i].DeployedApps[j].Resources = models.ResourceAllocation{
+					CPU:          resource.CPU,
+					CPUUsage:     resource.CPUUsage,
+					Memory:       resource.Memory,
+					MemoryUsage:  resource.MemoryUsage,
+					Storage:      resource.Storage,
+					StorageUsage: resource.StorageUsage,
+				}
+			}
+		}
+
+		// Deserialize config fields
+		if err := ds.deserializeConfigFields(deployments[i]); err != nil {
+			return nil, fmt.Errorf("failed to deserialize config fields: %w", err)
+		}
+	}
+
+	return deployments, nil
+}
+
+// GetByClientID retrieves deployments for a specific client
+func (ds *DeploymentStore) GetByClientID(clientID string) ([]*models.Deployment, error) {
+	return ds.List(map[string]interface{}{"client_id": clientID})
+}
+
+// GetByUserID retrieves deployments created by a specific user
+func (ds *DeploymentStore) GetByUserID(userID string) ([]*models.Deployment, error) {
+	return ds.List(map[string]interface{}{"created_by": userID})
+}
+
+// GetByTemplateID retrieves deployments based on a specific template
+func (ds *DeploymentStore) GetByTemplateID(templateID string) ([]*models.Deployment, error) {
+	return ds.List(map[string]interface{}{"template_id": templateID})
+}
+
+// serializeConfigFields serializes JSON config fields to strings
+func (ds *DeploymentStore) serializeConfigFields(deployment *models.Deployment) error {
+	// Serialize logs config if provided
+	if deployment.LogsConfig == "" {
+		logsConfig := models.LogConfiguration{
+			Enabled:       true,
+			RetentionDays: 7,
+		}
+		logsConfigBytes, err := json.Marshal(logsConfig)
+		if err != nil {
+			return err
+		}
+		deployment.LogsConfig = string(logsConfigBytes)
+	}
+
+	// Serialize metrics config if provided
+	if deployment.MetricsConfig == "" {
+		metricsConfig := models.MetricsConfiguration{
+			Enabled:       true,
+			RetentionDays: 30,
+		}
+		metricsConfigBytes, err := json.Marshal(metricsConfig)
+		if err != nil {
+			return err
+		}
+		deployment.MetricsConfig = string(metricsConfigBytes)
+	}
+
+	// Serialize alerts config if provided
+	if deployment.AlertsConfig == "" {
+		alertsConfig := []models.AlertConfiguration{}
+		alertsConfigBytes, err := json.Marshal(alertsConfig)
+		if err != nil {
+			return err
+		}
+		deployment.AlertsConfig = string(alertsConfigBytes)
+	}
+
+	return nil
+}
+
+// deserializeConfigFields deserializes JSON config fields from strings
+func (ds *DeploymentStore) deserializeConfigFields(deployment *models.Deployment) error {
+	// No need to deserialize in the store, as these fields are stored as strings
+	// in the database and are deserialized as needed by the service layer
+	return nil
+}

+ 102 - 0
dbstore/template.go

@@ -0,0 +1,102 @@
+package dbstore
+
+import (
+	"fmt"
+
+	"git.linuxforward.com/byop/byop-engine/dbmanager"
+	"git.linuxforward.com/byop/byop-engine/models"
+	"github.com/google/uuid"
+	"gorm.io/gorm"
+)
+
+// TemplateStore handles database operations for deployment templates
+type TemplateStore struct {
+	db *gorm.DB
+}
+
+// NewTemplateStore creates a new TemplateStore
+func NewTemplateStore(dbManager dbmanager.DbManager) *TemplateStore {
+	return &TemplateStore{
+		db: dbManager.GetDB(),
+	}
+}
+
+// Create creates a new template
+func (ts *TemplateStore) Create(template *models.Template) error {
+	// Generate ID if not provided
+	if template.ID == "" {
+		template.ID = uuid.New().String()
+	}
+
+	// GORM will handle created_at and updated_at automatically
+	return ts.db.Create(template).Error
+}
+
+// GetByID retrieves a template by ID
+func (ts *TemplateStore) GetByID(id string) (*models.Template, error) {
+	var template models.Template
+	result := ts.db.First(&template, "id = ?", id)
+	if result.Error != nil {
+		if result.Error == gorm.ErrRecordNotFound {
+			return nil, nil // No template found
+		}
+		return nil, fmt.Errorf("failed to get template: %w", result.Error)
+	}
+	return &template, nil
+}
+
+// Update updates an existing template
+func (ts *TemplateStore) Update(template *models.Template) error {
+	return ts.db.Save(template).Error
+}
+
+// Delete deletes a template by ID
+func (ts *TemplateStore) Delete(id string) error {
+	return ts.db.Delete(&models.Template{}, "id = ?", id).Error
+}
+
+// List retrieves all templates with optional filtering
+func (ts *TemplateStore) List(filter map[string]interface{}) ([]*models.Template, error) {
+	var templates []*models.Template
+
+	// Build query from filters
+	query := ts.db
+	if filter != nil {
+		for key, value := range filter {
+			query = query.Where(key+" = ?", value)
+		}
+	}
+
+	// Execute query
+	if err := query.Find(&templates).Error; err != nil {
+		return nil, fmt.Errorf("failed to list templates: %w", err)
+	}
+
+	return templates, nil
+}
+
+// GetTemplateWithDeployments retrieves a template by ID with associated deployments
+func (ts *TemplateStore) GetTemplateWithDeployments(id string) (*models.Template, error) {
+	var template models.Template
+	result := ts.db.Preload("Deployments").First(&template, "id = ?", id)
+	if result.Error != nil {
+		if result.Error == gorm.ErrRecordNotFound {
+			return nil, nil // No template found
+		}
+		return nil, fmt.Errorf("failed to get template: %w", result.Error)
+	}
+	return &template, nil
+}
+
+// GetByVersion retrieves a template by name and version
+func (ts *TemplateStore) GetByVersion(name string, version string) (*models.Template, error) {
+	var template models.Template
+	result := ts.db.Where("name = ? AND version = ?", name, version).First(&template)
+	if result.Error != nil {
+		if result.Error == gorm.ErrRecordNotFound {
+			return nil, nil // No template found
+		}
+		return nil, fmt.Errorf("failed to get template: %w", result.Error)
+	}
+	return &template, nil
+}

+ 66 - 101
dbstore/user.go

@@ -1,145 +1,110 @@
 package dbstore
 
 import (
-	"encoding/json"
-	"fmt"
-
 	"git.linuxforward.com/byop/byop-engine/dbmanager"
 	"git.linuxforward.com/byop/byop-engine/models"
+	"github.com/google/uuid"
+	"gorm.io/gorm"
 )
 
+// UserStore handles database operations for users
 type UserStore struct {
-	dbConn dbmanager.DbManager
+	db *gorm.DB
 }
 
 // NewUserStore creates a new UserStore
-func NewUserStore(dbConn dbmanager.DbManager) *UserStore {
+func NewUserStore(dbManager dbmanager.DbManager) *UserStore {
 	return &UserStore{
-		dbConn: dbConn,
-	}
-}
-
-func (us *UserStore) CreateTable() error {
-	query := `
-    CREATE TABLE IF NOT EXISTS users (
-        id TEXT PRIMARY KEY,
-        data TEXT NOT NULL
-    );`
-	_, err := us.dbConn.Exec(query)
-	if err != nil {
-		return fmt.Errorf("failed to create users table: %w", err)
+		db: dbManager.GetDB(),
 	}
-	return nil
 }
 
 // Create creates a new user
 func (us *UserStore) Create(user *models.User) error {
-	return us.dbConn.Create("users", user)
+	// Generate ID if not provided
+	if user.ID == "" {
+		user.ID = uuid.New().String()
+	}
+
+	// GORM will handle created_at and updated_at automatically
+	return us.db.Create(user).Error
 }
 
 // GetByID retrieves a user by ID
 func (us *UserStore) GetByID(id string) (*models.User, error) {
-	user, err := us.dbConn.GetByID("users", id)
-	if err != nil {
-		return nil, err
+	var user models.User
+	result := us.db.First(&user, "id = ?", id)
+	if result.Error != nil {
+		if result.Error == gorm.ErrRecordNotFound {
+			return nil, nil // No user found
+		}
+		return nil, result.Error
+	}
+	return &user, nil
+}
+
+// GetByUsername retrieves a user by ID
+func (us *UserStore) GetByUsername(id string) (*models.User, error) {
+	var user models.User
+	result := us.db.First(&user, "username = ?", id)
+	if result.Error != nil {
+		if result.Error == gorm.ErrRecordNotFound {
+			return nil, nil // No user found
+		}
+		return nil, result.Error
+	}
+	return &user, nil
+}
+
+// ListDeploymentsByUserID retrieves all deployments for a user by ID
+func (us *UserStore) ListDeploymentsByUserID(userID string) ([]*models.Deployment, error) {
+	var deployments []*models.Deployment
+	result := us.db.Where("user_id = ?", userID).Find(&deployments)
+	if result.Error != nil {
+		return nil, result.Error
 	}
-	return user.(*models.User), nil
+	return deployments, nil
 }
 
 // Update updates an existing user
 func (us *UserStore) Update(user *models.User) error {
-	return us.dbConn.Update("users", user)
+	return us.db.Save(user).Error
 }
 
 // Delete deletes a user by ID
 func (us *UserStore) Delete(id string) error {
-	return us.dbConn.Delete("users", id)
+	return us.db.Delete(&models.User{}, "id = ?", id).Error
 }
 
-// List retrieves all users
+// List retrieves all users with optional filtering
 func (us *UserStore) List(filter map[string]interface{}) ([]*models.User, error) {
-	users, err := us.dbConn.List("users", filter)
-	if err != nil {
-		return nil, err
-	}
+	var users []*models.User
 
-	// If no users are found, return an empty slice
-	if len(users) == 0 {
-		return []*models.User{}, nil
-	}
-	var userList []*models.User
-	for _, user := range users {
-		userData, ok := user.(map[string]interface{})
-		if !ok {
-			return nil, fmt.Errorf("unexpected data format")
+	// Build query from filters
+	query := us.db
+	if filter != nil {
+		for key, value := range filter {
+			query = query.Where(key+" = ?", value)
 		}
-
-		// Unmarshal the map into a *models.User
-		user := &models.User{}
-		jsonData, err := json.Marshal(userData)
-		if err != nil {
-			return nil, fmt.Errorf("failed to marshal user data: %w", err)
-		}
-
-		if err := json.Unmarshal(jsonData, user); err != nil {
-			return nil, fmt.Errorf("failed to unmarshal user data: %w", err)
-		}
-		userList = append(userList, user)
 	}
-	return userList, nil
-}
 
-// GetByUsername retrieves a user by username
-func (us *UserStore) GetByUsername(username string) (*models.User, error) {
-	user, err := us.dbConn.GetByID("users", username)
-	if err != nil {
+	// Execute query
+	if err := query.Find(&users).Error; err != nil {
 		return nil, err
 	}
-	return user.(*models.User), nil
-}
-
-func (us *UserStore) GetUserByEmail(email string) (*models.User, error) {
-	// Retrieve the list of users filtered by email
-	users, err := us.dbConn.List("users", map[string]interface{}{"email": email})
-	if err != nil {
-		return nil, err
-	}
-
-	// If no users are found, return nil
-	if len(users) == 0 {
-		return nil, nil
-	}
-
-	// Convert the first result to *models.User
-	userData, ok := users[0].(map[string]interface{})
-	if !ok {
-		return nil, fmt.Errorf("unexpected data format")
-	}
 
-	// Unmarshal the map into a *models.User
-	user := &models.User{}
-	jsonData, err := json.Marshal(userData)
-	if err != nil {
-		return nil, fmt.Errorf("failed to marshal user data: %w", err)
-	}
-
-	if err := json.Unmarshal(jsonData, user); err != nil {
-		return nil, fmt.Errorf("failed to unmarshal user data: %w", err)
-	}
-
-	return user, nil
+	return users, nil
 }
 
-// ListDeploymentsByUserID retrieves all deployments for a user by user ID
-func (us *UserStore) ListDeploymentsByUserID(userID string) ([]*models.Deployment, error) {
-	deployments, err := us.dbConn.List("deployments", map[string]interface{}{"user_id": userID})
-	if err != nil {
-		return nil, err
-	}
-
-	var deploymentList []*models.Deployment
-	for _, deployment := range deployments {
-		deploymentList = append(deploymentList, deployment.(*models.Deployment))
+// GetUserByEmail retrieves a user by email
+func (us *UserStore) GetUserByEmail(email string) (*models.User, error) {
+	var user models.User
+	result := us.db.Where("email = ?", email).First(&user)
+	if result.Error != nil {
+		if result.Error == gorm.ErrRecordNotFound {
+			return nil, nil // No user found
+		}
+		return nil, result.Error
 	}
-	return deploymentList, nil
+	return &user, nil
 }

+ 24 - 21
go.mod

@@ -5,53 +5,56 @@ go 1.24.2
 require (
 	github.com/gin-gonic/gin v1.10.0
 	github.com/golang-jwt/jwt v3.2.2+incompatible
-	github.com/google/uuid v1.6.0
 	github.com/ovh/go-ovh v1.7.0
 	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.22.0
 	gopkg.in/yaml.v3 v3.0.1
+	gorm.io/driver/sqlite v1.5.7
+	gorm.io/gorm v1.26.1
 )
 
 require (
 	github.com/beorn7/perks v1.0.1 // indirect
-	github.com/bytedance/sonic v1.11.6 // indirect
-	github.com/bytedance/sonic/loader v0.1.1 // indirect
+	github.com/bytedance/sonic v1.13.2 // indirect
+	github.com/bytedance/sonic/loader v0.2.4 // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
-	github.com/cloudwego/base64x v0.1.4 // indirect
-	github.com/cloudwego/iasm v0.2.0 // indirect
-	github.com/gabriel-vasile/mimetype v1.4.3 // indirect
-	github.com/gin-contrib/sse v0.1.0 // indirect
+	github.com/cloudwego/base64x v0.1.5 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.9 // indirect
+	github.com/gin-contrib/sse v1.1.0 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
-	github.com/go-playground/validator/v10 v10.20.0 // indirect
-	github.com/goccy/go-json v0.10.2 // indirect
+	github.com/go-playground/validator/v10 v10.26.0 // indirect
+	github.com/goccy/go-json v0.10.5 // indirect
+	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/klauspost/cpuid/v2 v2.2.7 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.10 // indirect
 	github.com/kr/text v0.2.0 // indirect
 	github.com/leodido/go-urn v1.4.0 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
-	github.com/prometheus/client_model v0.6.1 // indirect
-	github.com/prometheus/common v0.62.0 // indirect
-	github.com/prometheus/procfs v0.15.1 // indirect
+	github.com/prometheus/client_model v0.6.2 // indirect
+	github.com/prometheus/common v0.63.0 // indirect
+	github.com/prometheus/procfs v0.16.1 // indirect
 	github.com/rogpeppe/go-internal v1.14.1 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.12 // indirect
-	golang.org/x/arch v0.8.0 // indirect
-	golang.org/x/text v0.24.0 // indirect
+	golang.org/x/arch v0.17.0 // indirect
+	golang.org/x/text v0.25.0 // indirect
 )
 
 require (
 	github.com/golang-jwt/jwt/v5 v5.2.2
-	github.com/mattn/go-sqlite3 v1.14.28
+	github.com/google/uuid v1.6.0
+	github.com/mattn/go-sqlite3 v1.14.28 // indirect
 	github.com/pelletier/go-toml/v2 v2.2.4 // indirect
 	github.com/sirupsen/logrus v1.9.3
-	golang.org/x/crypto v0.37.0
-	golang.org/x/net v0.39.0 // indirect
-	golang.org/x/oauth2 v0.24.0 // indirect
-	golang.org/x/sys v0.32.0 // indirect
-	google.golang.org/protobuf v1.36.5 // indirect
+	golang.org/x/crypto v0.38.0
+	golang.org/x/net v0.40.0 // indirect
+	golang.org/x/oauth2 v0.30.0 // indirect
+	golang.org/x/sys v0.33.0 // indirect
+	google.golang.org/protobuf v1.36.6 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 )

+ 44 - 39
go.sum

@@ -1,23 +1,23 @@
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
-github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
-github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
+github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
+github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
 github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
+github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
-github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
-github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
+github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
+github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
 github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
-github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
-github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
-github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
+github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
+github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
+github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
 github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
 github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
 github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@@ -26,10 +26,10 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
-github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
-github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
-github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
+github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
+github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
+github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
 github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
 github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
@@ -41,11 +41,15 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
 github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
-github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
+github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
 github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -76,12 +80,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
 github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
-github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
-github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
-github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
-github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
-github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
+github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
 github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
 github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
@@ -100,24 +104,22 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
 github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
 github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
 github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
-golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
-golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
-golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
-golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
-golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
-golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
-golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
-golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU=
+golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
+golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
+golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
+golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
+golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
+golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
+golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
-golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
-golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
-google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
-google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
+golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
+golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
+google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -126,5 +128,8 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
+gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
+gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw=
+gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
 nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

+ 138 - 0
handlers/apps.go

@@ -0,0 +1,138 @@
+package handlers
+
+import (
+	"fmt"
+	"net/http"
+
+	"git.linuxforward.com/byop/byop-engine/models"
+	"git.linuxforward.com/byop/byop-engine/services"
+	"github.com/gin-gonic/gin"
+)
+
+// AppHandler handles application-related operations
+type AppHandler struct {
+	service *services.AppService
+}
+
+// NewAppHandler creates a new AppHandler
+func NewAppHandler(service *services.AppService) *AppHandler {
+	return &AppHandler{
+		service: service,
+	}
+}
+
+// RegisterRoutes registers routes for app operations
+func (h *AppHandler) RegisterRoutes(r *gin.RouterGroup) {
+	r.GET("/", h.ListApps)
+	r.POST("/", h.CreateApp)
+	r.GET("/:id", h.GetApp)
+	r.PUT("/:id", h.UpdateApp)
+	r.DELETE("/:id", h.DeleteApp)
+	r.GET("/:id/deployments", h.GetAppDeployments)
+}
+
+// ListApps returns all applications with optional filtering
+func (h *AppHandler) ListApps(c *gin.Context) {
+	filter := make(map[string]interface{})
+
+	// Attempt to bind query parameters, but allow empty filters
+	if err := c.ShouldBindQuery(&filter); err != nil && len(filter) > 0 {
+		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query parameters"})
+		return
+	}
+
+	apps, err := h.service.ListApps(filter)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to list applications: %v", err)})
+		return
+	}
+
+	c.JSON(http.StatusOK, apps)
+}
+
+// CreateApp creates a new application
+func (h *AppHandler) CreateApp(c *gin.Context) {
+	var app models.App
+
+	if err := c.ShouldBindJSON(&app); err != nil {
+		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid request body: %v", err)})
+		return
+	}
+
+	// Get the user ID from the context (set by auth middleware)
+	userID, exists := c.Get("userID")
+	if exists {
+		app.CreatedBy = userID.(string)
+	}
+
+	if err := h.service.CreateApp(&app); err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to create application: %v", err)})
+		return
+	}
+
+	c.JSON(http.StatusCreated, app)
+}
+
+// GetApp returns a specific application
+func (h *AppHandler) GetApp(c *gin.Context) {
+	id := c.Param("id")
+
+	app, err := h.service.GetApp(id)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to fetch application: %v", err)})
+		return
+	}
+
+	if app == nil {
+		c.JSON(http.StatusNotFound, gin.H{"error": "Application not found"})
+		return
+	}
+
+	c.JSON(http.StatusOK, app)
+}
+
+// UpdateApp updates an application
+func (h *AppHandler) UpdateApp(c *gin.Context) {
+	id := c.Param("id")
+
+	var updatedApp models.App
+	if err := c.ShouldBindJSON(&updatedApp); err != nil {
+		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid request body: %v", err)})
+		return
+	}
+
+	// Ensure the ID matches the URL parameter
+	updatedApp.ID = id
+
+	if err := h.service.UpdateApp(&updatedApp); err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to update application: %v", err)})
+		return
+	}
+
+	c.JSON(http.StatusOK, updatedApp)
+}
+
+// DeleteApp deletes an application
+func (h *AppHandler) DeleteApp(c *gin.Context) {
+	id := c.Param("id")
+
+	if err := h.service.DeleteApp(id); err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to delete application: %v", err)})
+		return
+	}
+
+	c.Status(http.StatusNoContent)
+}
+
+// GetAppDeployments returns all deployments for an application
+func (h *AppHandler) GetAppDeployments(c *gin.Context) {
+	id := c.Param("id")
+
+	deployments, err := h.service.GetAppDeployments(id)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to fetch application deployments: %v", err)})
+		return
+	}
+
+	c.JSON(http.StatusOK, deployments)
+}

+ 32 - 6
handlers/auth.go

@@ -53,22 +53,48 @@ func (h *AuthHandler) Login(c *gin.Context) {
 		return
 	}
 
-	// TODO: Implement authentication logic
-	resp, err := h.authService.GenerateToken(c, credentials.Email, "owner")
+	// Generate token for authentication
+	tokenResp, err := h.authService.GenerateToken(c, credentials.Email, string(user.Role))
 	if err != nil {
 		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
 		return
 	}
 
-	c.JSON(http.StatusOK, resp)
+	// Construct the new response format
+	response := map[string]interface{}{
+		"token":        tokenResp.AccessToken,
+		"refreshToken": tokenResp.RefreshToken,
+		"user": map[string]interface{}{
+			"id":       user.ID,
+			"username": user.Username,
+			"email":    user.Email,
+			"role":     user.Role,
+			"preferences": map[string]interface{}{
+				"theme":         user.Preferences.Theme,
+				"notifications": user.Preferences.Notifications,
+			},
+		},
+	}
+
+	c.JSON(http.StatusOK, response)
 }
 
 // RefreshToken handles token refresh
 func (h *AuthHandler) RefreshToken(c *gin.Context) {
-	// TODO: Implement token refresh logic
+	var refreshRequest struct {
+		RefreshToken string `json:"refresh_token" binding:"required"`
+	}
+
+	if err := c.ShouldBindJSON(&refreshRequest); err != nil {
+		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
+		return
+	}
 
-	resp := gin.H{
-		"token": "new-dummy-token",
+	// Validate refresh token and generate new access token
+	resp, err := h.authService.RefreshToken(c, refreshRequest.RefreshToken)
+	if err != nil {
+		c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired refresh token"})
+		return
 	}
 
 	c.JSON(http.StatusOK, resp)

+ 0 - 250
handlers/autodeploy.go

@@ -1,250 +0,0 @@
-package handlers
-
-import (
-	"encoding/json"
-	"fmt"
-	"log"
-	"net/http"
-	"sync"
-	"time"
-
-	"git.linuxforward.com/byop/byop-engine/models"
-	"github.com/gin-gonic/gin"
-)
-
-// AutoDeployHandler handles auto-deployment related operations
-type AutoDeployHandler struct {
-	autoDeploySettings *models.AutoDeploySettings
-	settingsMutex      sync.RWMutex
-	deploymentQueue    chan *models.AutoDeployRequest
-}
-
-// NewAutoDeployHandler creates a new AutoDeployHandler
-func NewAutoDeployHandler() *AutoDeployHandler {
-	return &AutoDeployHandler{}
-}
-
-// RegisterRoutes registers routes for auto-deployment operations
-func (h *AutoDeployHandler) RegisterRoutes(r *gin.RouterGroup) {
-	r.POST("/client", h.AutoDeployClient)
-	r.POST("/webhook", h.AutoDeployWebhook)
-	r.PUT("/settings", h.UpdateAutoDeploySettings)
-	r.GET("/settings", h.GetAutoDeploySettings)
-}
-
-// AutoDeployClient handles requests to deploy for a client
-func (h *AutoDeployHandler) AutoDeployClient(c *gin.Context) {
-	// Parse the request
-	var request *models.AutoDeployRequest
-	if err := c.ShouldBindJSON(&request); err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{
-			"error": "Invalid request body",
-		})
-		return
-	}
-
-	// Check if auto-deployment is enabled
-	h.settingsMutex.RLock()
-	enabled := h.autoDeploySettings.Enabled
-	h.settingsMutex.RUnlock()
-
-	if !enabled {
-		c.JSON(http.StatusServiceUnavailable, gin.H{
-			"error": "Auto-deployment is currently disabled",
-		})
-		return
-	}
-
-	// Queue the deployment request
-	select {
-	case h.deploymentQueue <- request:
-		// Successfully queued
-	default:
-		// Queue is full
-		c.JSON(http.StatusServiceUnavailable, gin.H{
-			"error": "Deployment queue is full, try again later",
-		})
-		return
-	}
-
-	// Return a success response
-	c.JSON(http.StatusAccepted, gin.H{
-		"status":   "queued",
-		"message":  "Deployment request has been queued",
-		"clientId": request.ClientID,
-		"queuedAt": time.Now(),
-	})
-}
-
-// AutoDeployWebhook handles webhook triggers for auto-deployment
-func (h *AutoDeployHandler) AutoDeployWebhook(c *gin.Context) {
-	// Parse the webhook payload
-	var payload *models.WebhookPayload
-	if err := c.ShouldBindJSON(&payload); err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{
-			"error": "Invalid webhook payload",
-		})
-		return
-	}
-
-	// Validate the webhook signature
-	h.settingsMutex.RLock()
-	secret := h.autoDeploySettings.WebhookSecret
-	enabled := h.autoDeploySettings.Enabled
-	h.settingsMutex.RUnlock()
-
-	// This is a simplified signature check, in production use HMAC
-	if c.GetHeader("X-Webhook-Signature") != secret {
-		c.JSON(http.StatusUnauthorized, gin.H{
-			"error": "Invalid webhook signature",
-		})
-		return
-	}
-
-	if !enabled {
-		c.JSON(http.StatusServiceUnavailable, gin.H{
-			"error": "Auto-deployment is currently disabled",
-		})
-		return
-	}
-
-	// Handle different event types
-	switch payload.Event {
-	case "client.created":
-		// Check if we should auto-deploy for new clients
-		h.settingsMutex.RLock()
-		autoDeploy := h.autoDeploySettings.AutoDeployNewClients
-		h.settingsMutex.RUnlock()
-
-		if autoDeploy && payload.ClientID != "" {
-			// Queue a deployment request
-			request := &models.AutoDeployRequest{
-				ClientID: payload.ClientID,
-				// Use defaults for other fields
-			}
-
-			select {
-			case h.deploymentQueue <- request:
-				// Successfully queued
-			default:
-				// Queue is full
-				log.Printf("Deployment queue is full, skipping auto-deployment for client %s", payload.ClientID)
-			}
-		}
-
-	case "deployment.requested":
-		// Handle explicit deployment requests
-		if payload.ClientID == "" {
-			c.JSON(http.StatusBadRequest, gin.H{
-				"error": "client_id is required for deployment requests",
-			})
-			return
-		}
-
-		// Parse the deployment data
-		var deployData struct {
-			TemplateID string            `json:"template_id"`
-			ProviderID string            `json:"provider_id"`
-			Region     string            `json:"region"`
-			Tags       map[string]string `json:"tags"`
-		}
-
-		if err := json.Unmarshal(payload.Data, &deployData); err != nil {
-			c.JSON(http.StatusBadRequest, gin.H{
-				"error": "Invalid deployment data",
-			})
-			return
-		}
-
-		// Queue the deployment
-		request := &models.AutoDeployRequest{
-			ClientID:   payload.ClientID,
-			TemplateID: deployData.TemplateID,
-			ProviderID: deployData.ProviderID,
-			Region:     deployData.Region,
-			Tags:       deployData.Tags,
-		}
-
-		select {
-		case h.deploymentQueue <- request:
-			// Successfully queued
-		default:
-			// Queue is full
-			c.JSON(http.StatusServiceUnavailable, gin.H{
-				"error": "Deployment queue is full, try again later",
-			})
-			return
-		}
-
-	default:
-		// Unknown event type
-		c.JSON(http.StatusBadRequest, gin.H{
-			"error": fmt.Sprintf("Unsupported event type: %s", payload.Event),
-		})
-		return
-	}
-
-	// Return a success response
-	c.JSON(http.StatusOK, gin.H{
-		"status":    "received",
-		"event":     payload.Event,
-		"timestamp": time.Now(),
-		"requestId": payload.RequestID,
-	})
-}
-
-// UpdateAutoDeploySettings updates the auto-deployment settings
-func (h *AutoDeployHandler) UpdateAutoDeploySettings(c *gin.Context) {
-	// Parse the settings
-	var settings *models.AutoDeploySettings
-	if err := c.ShouldBindJSON(&settings); err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{
-			"error": "Invalid settings format",
-		})
-		return
-	}
-
-	// Validate settings
-	if settings.DefaultProviderID == "" {
-		c.JSON(http.StatusBadRequest, gin.H{
-			"error": "default_provider_id is required",
-		})
-		return
-	}
-
-	if settings.DefaultTemplateID == "" {
-		c.JSON(http.StatusBadRequest, gin.H{
-			"error": "default_template_id is required",
-		})
-		return
-	}
-
-	// Update the settings
-	h.settingsMutex.Lock()
-	// Keep the webhook secret if not provided
-	if settings.WebhookSecret == "" {
-		settings.WebhookSecret = h.autoDeploySettings.WebhookSecret
-	}
-	h.autoDeploySettings = settings
-	h.settingsMutex.Unlock()
-
-	// Return the updated settings (without sensitive fields)
-	h.settingsMutex.RLock()
-	responseSettings := h.autoDeploySettings
-	responseSettings.WebhookSecret = "********" // Hide the actual secret
-	h.settingsMutex.RUnlock()
-
-	c.JSON(http.StatusOK, responseSettings)
-}
-
-// GetAutoDeploySettings returns the current auto-deployment settings
-func (h *AutoDeployHandler) GetAutoDeploySettings(c *gin.Context) {
-	// Get a copy of the settings
-	h.settingsMutex.RLock()
-	settings := h.autoDeploySettings
-	settings.WebhookSecret = "********" // Hide the actual secret
-	h.settingsMutex.RUnlock()
-
-	// Return the settings
-	c.JSON(http.StatusOK, settings)
-}

+ 8 - 10
handlers/clients.go

@@ -21,18 +21,16 @@ func NewClientHandler(service *services.ClientService) *ClientHandler {
 	}
 }
 
-// RegisterRoutes registers routes for client operations
-func (h *ClientHandler) RegisterRoutes(r *gin.RouterGroup) {
-	r.GET("/", h.ListClients)
-	r.POST("/", h.CreateClient)
-	r.GET("/:id", h.GetClient)
-	r.PUT("/:id", h.UpdateClient)
-	r.DELETE("/:id", h.DeleteClient)
-	r.GET("/:id/deployments", h.GetClientDeployments)
-}
-
 // ListClients returns all clients
 func (h *ClientHandler) ListClients(c *gin.Context) {
+	filter := make(map[string]interface{})
+
+	// Attempt to bind query parameters, but allow empty filters
+	if err := c.ShouldBindQuery(&filter); err != nil && len(filter) > 0 {
+		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query parameters"})
+		return
+	}
+
 	clients, err := h.service.ListClients(nil)
 	if err != nil {
 		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to fetch clients: %v", err)})

+ 96 - 89
handlers/deployments.go

@@ -1,20 +1,24 @@
 package handlers
 
 import (
+	"fmt"
 	"net/http"
 
 	"git.linuxforward.com/byop/byop-engine/models"
+	"git.linuxforward.com/byop/byop-engine/services"
 	"github.com/gin-gonic/gin"
 )
 
 // DeploymentHandler handles deployment-related operations
 type DeploymentHandler struct {
-	// Add any dependencies needed for deployment operations
+	service *services.DeploymentService
 }
 
 // NewDeploymentHandler creates a new DeploymentHandler
-func NewDeploymentHandler() *DeploymentHandler {
-	return &DeploymentHandler{}
+func NewDeploymentHandler(service *services.DeploymentService) *DeploymentHandler {
+	return &DeploymentHandler{
+		service: service,
+	}
 }
 
 // RegisterRoutes registers routes for deployment operations
@@ -24,18 +28,27 @@ func (h *DeploymentHandler) RegisterRoutes(r *gin.RouterGroup) {
 	r.GET("/:id", h.GetDeployment)
 	r.PUT("/:id", h.UpdateDeployment)
 	r.DELETE("/:id", h.DeleteDeployment)
-	r.POST("/:id/start", h.StartDeployment)
-	r.POST("/:id/stop", h.StopDeployment)
-	r.POST("/:id/restart", h.RestartDeployment)
-	r.GET("/:id/logs", h.GetDeploymentLogs)
-	r.GET("/:id/metrics", h.GetDeploymentMetrics)
-	r.POST("/:id/scale", h.ScaleDeployment)
+	r.PUT("/:id/status", h.UpdateDeploymentStatus)
+	r.GET("/by-client/:clientId", h.GetDeploymentsByClient)
+	r.GET("/by-template/:templateId", h.GetDeploymentsByTemplate)
+	r.GET("/by-user/:userId", h.GetDeploymentsByUser)
 }
 
-// ListDeployments returns all deployments
+// ListDeployments returns all deployments with optional filtering
 func (h *DeploymentHandler) ListDeployments(c *gin.Context) {
-	// TODO: Fetch deployments from database
-	deployments := []models.Deployment{}
+	filter := make(map[string]interface{})
+
+	// Attempt to bind query parameters, but allow empty filters
+	if err := c.ShouldBindQuery(&filter); err != nil && len(filter) > 0 {
+		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query parameters"})
+		return
+	}
+
+	deployments, err := h.service.ListDeployments(filter)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to list deployments: %v", err)})
+		return
+	}
 
 	c.JSON(http.StatusOK, deployments)
 }
@@ -45,11 +58,20 @@ func (h *DeploymentHandler) CreateDeployment(c *gin.Context) {
 	var deployment models.Deployment
 
 	if err := c.ShouldBindJSON(&deployment); err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
+		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid request body: %v", err)})
 		return
 	}
 
-	// TODO: Save deployment to database
+	// Get the user ID from the context (set by auth middleware)
+	userID, exists := c.Get("userID")
+	if exists {
+		deployment.CreatedBy = userID.(string)
+	}
+
+	if err := h.service.CreateDeployment(&deployment); err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to create deployment: %v", err)})
+		return
+	}
 
 	c.JSON(http.StatusCreated, deployment)
 }
@@ -57,13 +79,17 @@ func (h *DeploymentHandler) CreateDeployment(c *gin.Context) {
 // GetDeployment returns a specific deployment
 func (h *DeploymentHandler) GetDeployment(c *gin.Context) {
 	id := c.Param("id")
-	if id == "" {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing deployment ID"})
+
+	deployment, err := h.service.GetDeployment(id)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to fetch deployment: %v", err)})
 		return
 	}
 
-	// TODO: Fetch deployment from database
-	deployment := models.Deployment{ID: id}
+	if deployment == nil {
+		c.JSON(http.StatusNotFound, gin.H{"error": "Deployment not found"})
+		return
+	}
 
 	c.JSON(http.StatusOK, deployment)
 }
@@ -71,111 +97,92 @@ func (h *DeploymentHandler) GetDeployment(c *gin.Context) {
 // UpdateDeployment updates a deployment
 func (h *DeploymentHandler) UpdateDeployment(c *gin.Context) {
 	id := c.Param("id")
-	if id == "" {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing deployment ID"})
+
+	var updatedDeployment models.Deployment
+	if err := c.ShouldBindJSON(&updatedDeployment); err != nil {
+		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid request body: %v", err)})
 		return
 	}
 
-	var deployment models.Deployment
-	if err := c.ShouldBindJSON(&deployment); err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
+	// Ensure the ID matches the URL parameter
+	updatedDeployment.ID = id
+
+	if err := h.service.UpdateDeployment(&updatedDeployment); err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to update deployment: %v", err)})
 		return
 	}
 
-	deployment.ID = id
-	// TODO: Update deployment in database
-
-	c.JSON(http.StatusOK, deployment)
+	c.JSON(http.StatusOK, updatedDeployment)
 }
 
 // DeleteDeployment deletes a deployment
 func (h *DeploymentHandler) DeleteDeployment(c *gin.Context) {
-	// TODO: Delete deployment from database
-
-	c.Status(http.StatusNoContent)
-}
-
-// StartDeployment starts a deployment
-func (h *DeploymentHandler) StartDeployment(c *gin.Context) {
 	id := c.Param("id")
-	if id == "" {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing deployment ID"})
+
+	if err := h.service.DeleteDeployment(id); err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to delete deployment: %v", err)})
 		return
 	}
 
-	// TODO: Start deployment
-	result := map[string]string{"status": "started", "id": id}
-
-	c.JSON(http.StatusOK, result)
+	c.Status(http.StatusNoContent)
 }
 
-// StopDeployment stops a deployment
-func (h *DeploymentHandler) StopDeployment(c *gin.Context) {
+// UpdateDeploymentStatus updates the status of a deployment
+func (h *DeploymentHandler) UpdateDeploymentStatus(c *gin.Context) {
 	id := c.Param("id")
-	if id == "" {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing deployment ID"})
-		return
-	}
-
-	// TODO: Stop deployment
-	result := map[string]string{"status": "stopped", "id": id}
 
-	c.JSON(http.StatusOK, result)
-}
+	var statusUpdate struct {
+		Status string `json:"status" binding:"required"`
+	}
 
-// RestartDeployment restarts a deployment
-func (h *DeploymentHandler) RestartDeployment(c *gin.Context) {
-	id := c.Param("id")
-	if id == "" {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing deployment ID"})
+	if err := c.ShouldBindJSON(&statusUpdate); err != nil {
+		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid request body: %v", err)})
 		return
 	}
 
-	// TODO: Restart deployment
-	result := map[string]string{"status": "restarted", "id": id}
+	if err := h.service.UpdateDeploymentStatus(id, statusUpdate.Status); err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to update deployment status: %v", err)})
+		return
+	}
 
-	c.JSON(http.StatusOK, result)
+	c.Status(http.StatusOK)
 }
 
-// GetDeploymentLogs returns logs for a deployment
-func (h *DeploymentHandler) GetDeploymentLogs(c *gin.Context) {
-	// TODO: Fetch deployment logs
-	logs := []string{"Log entry 1", "Log entry 2"}
-
-	c.JSON(http.StatusOK, logs)
-}
+// GetDeploymentsByClient returns all deployments for a specific client
+func (h *DeploymentHandler) GetDeploymentsByClient(c *gin.Context) {
+	clientID := c.Param("clientId")
 
-// GetDeploymentMetrics returns metrics for a deployment
-func (h *DeploymentHandler) GetDeploymentMetrics(c *gin.Context) {
-	// TODO: Fetch deployment metrics
-	metrics := []models.MetricSample{}
+	deployments, err := h.service.GetDeploymentsByClientID(clientID)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to fetch client deployments: %v", err)})
+		return
+	}
 
-	c.JSON(http.StatusOK, metrics)
+	c.JSON(http.StatusOK, deployments)
 }
 
-// ScaleDeployment scales a deployment
-func (h *DeploymentHandler) ScaleDeployment(c *gin.Context) {
-	id := c.Param("id")
-	if id == "" {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing deployment ID"})
+// GetDeploymentsByTemplate returns all deployments for a specific template
+func (h *DeploymentHandler) GetDeploymentsByTemplate(c *gin.Context) {
+	templateID := c.Param("templateId")
+
+	deployments, err := h.service.GetDeploymentsByTemplateID(templateID)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to fetch template deployments: %v", err)})
 		return
 	}
 
-	var scaleRequest struct {
-		Replicas int `json:"replicas"`
-	}
+	c.JSON(http.StatusOK, deployments)
+}
 
-	if err := c.ShouldBindJSON(&scaleRequest); err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
-		return
-	}
+// GetDeploymentsByUser returns all deployments created by a specific user
+func (h *DeploymentHandler) GetDeploymentsByUser(c *gin.Context) {
+	userID := c.Param("userId")
 
-	// TODO: Scale deployment
-	result := map[string]interface{}{
-		"id":       id,
-		"replicas": scaleRequest.Replicas,
-		"status":   "scaling",
+	deployments, err := h.service.GetDeploymentsByUserID(userID)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to fetch user deployments: %v", err)})
+		return
 	}
 
-	c.JSON(http.StatusOK, result)
+	c.JSON(http.StatusOK, deployments)
 }

+ 0 - 129
handlers/monitoring.go

@@ -1,129 +0,0 @@
-package handlers
-
-import (
-	"fmt"
-	"net/http"
-
-	"github.com/gin-gonic/gin"
-)
-
-// MonitoringHandler handles monitoring-related operations
-type MonitoringHandler struct {
-	// Add any dependencies needed for monitoring operations
-}
-
-// NewMonitoringHandler creates a new MonitoringHandler
-func NewMonitoringHandler() *MonitoringHandler {
-	return &MonitoringHandler{}
-}
-
-// RegisterRoutes registers routes for monitoring operations
-func (h *MonitoringHandler) RegisterRoutes(r *gin.RouterGroup) {
-	r.GET("/overview", h.MonitoringOverview)
-	r.GET("/deployments/:id", h.MonitoringDeployment)
-	r.GET("/alerts", h.ListAlerts)
-	r.POST("/alerts", h.CreateAlert)
-	r.GET("/alerts/:id", h.GetAlert)
-	r.PUT("/alerts/:id", h.UpdateAlert)
-	r.DELETE("/alerts/:id", h.DeleteAlert)
-}
-
-// MonitoringOverview returns an overview of system monitoring
-func (h *MonitoringHandler) MonitoringOverview(c *gin.Context) {
-	overview := map[string]interface{}{
-		"total_deployments":    10,
-		"active_deployments":   8,
-		"inactive_deployments": 2,
-		"alerts":               1,
-		"avg_cpu_usage":        45.2,
-		"avg_memory_usage":     60.1,
-	}
-
-	c.JSON(http.StatusOK, overview)
-}
-
-// MonitoringDeployment returns monitoring data for a deployment
-func (h *MonitoringHandler) MonitoringDeployment(c *gin.Context) {
-	id := c.Param("id")
-
-	data := map[string]interface{}{
-		"deployment_id": id,
-		"cpu_usage":     42.5,
-		"memory_usage":  58.7,
-		"disk_usage":    30.2,
-		"network_in":    1024,
-		"network_out":   2048,
-		"uptime":        "10d 4h 30m",
-	}
-
-	c.JSON(http.StatusOK, data)
-}
-
-// ListAlerts returns all monitoring alerts
-func (h *MonitoringHandler) ListAlerts(c *gin.Context) {
-	alerts := []map[string]interface{}{
-		{
-			"id":            "alert-id",
-			"name":          "High CPU Usage",
-			"condition":     "cpu_usage > 80",
-			"deployment_id": "deployment-id",
-			"status":        "active",
-		},
-	}
-
-	c.JSON(http.StatusOK, alerts)
-}
-
-// CreateAlert creates a new monitoring alert
-func (h *MonitoringHandler) CreateAlert(c *gin.Context) {
-	var alert map[string]interface{}
-
-	if err := c.ShouldBindJSON(&alert); err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
-		return
-	}
-
-	alert["id"] = "new-alert-id"
-
-	c.JSON(http.StatusCreated, alert)
-}
-
-// GetAlert returns a specific alert
-func (h *MonitoringHandler) GetAlert(c *gin.Context) {
-	id := c.Param("id")
-
-	alert := map[string]interface{}{
-		"id":            id,
-		"name":          "High CPU Usage",
-		"condition":     "cpu_usage > 80",
-		"deployment_id": "deployment-id",
-		"status":        "active",
-	}
-
-	c.JSON(http.StatusOK, alert)
-}
-
-// UpdateAlert updates a monitoring alert
-func (h *MonitoringHandler) UpdateAlert(c *gin.Context) {
-	id := c.Param("id")
-
-	var alert map[string]interface{}
-	if err := c.ShouldBindJSON(&alert); err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
-		return
-	}
-
-	alert["id"] = id
-
-	c.JSON(http.StatusOK, alert)
-}
-
-// DeleteAlert deletes a monitoring alert
-func (h *MonitoringHandler) DeleteAlert(c *gin.Context) {
-	id := c.Param("id")
-
-	fmt.Println("Deleting alert ID:", id)
-
-	// Log deletion (optional)
-	c.Status(http.StatusNoContent)
-}

+ 87 - 43
handlers/templates.go

@@ -5,45 +5,61 @@ import (
 	"net/http"
 
 	"git.linuxforward.com/byop/byop-engine/models"
+	"git.linuxforward.com/byop/byop-engine/services"
 	"github.com/gin-gonic/gin"
 )
 
 // TemplateHandler handles template-related operations
 type TemplateHandler struct {
-	// Add any dependencies needed for template operations
+	service *services.TemplateService
 }
 
 // NewTemplateHandler creates a new TemplateHandler
-func NewTemplateHandler() *TemplateHandler {
-	return &TemplateHandler{}
-}
-
-// RegisterRoutes registers routes for template operations
-func (h *TemplateHandler) RegisterRoutes(r *gin.RouterGroup) {
-	r.GET("/", h.ListTemplates)
-	r.POST("/", h.CreateTemplate)
-	r.GET("/:id", h.GetTemplate)
-	r.PUT("/:id", h.UpdateTemplate)
-	r.DELETE("/:id", h.DeleteTemplate)
-	r.POST("/:id/deploy", h.DeployTemplate)
+func NewTemplateHandler(service *services.TemplateService) *TemplateHandler {
+	return &TemplateHandler{
+		service: service,
+	}
 }
 
-// ListTemplates returns all templates
+// ListTemplates returns all templates with optional filtering
 func (h *TemplateHandler) ListTemplates(c *gin.Context) {
-	// Implementation for listing templates
-	templates := []models.Template{}
+	filter := make(map[string]interface{})
+
+	// Attempt to bind query parameters, but allow empty filters
+	if err := c.ShouldBindQuery(&filter); err != nil && len(filter) > 0 {
+		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query parameters"})
+		return
+	}
+
+	templates, err := h.service.ListTemplates(filter)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to list templates: %v", err)})
+		return
+	}
+
 	c.JSON(http.StatusOK, templates)
 }
 
-// CreateTemplate creates a new template
+// CreateTemplate creates a new deployment template
 func (h *TemplateHandler) CreateTemplate(c *gin.Context) {
 	var template models.Template
+
 	if err := c.ShouldBindJSON(&template); err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
+		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid request body: %v", err)})
+		return
+	}
+
+	// Get the user ID from the context (set by auth middleware)
+	userID, exists := c.Get("userID")
+	if exists {
+		template.CreatedBy = userID.(string)
+	}
+
+	if err := h.service.CreateTemplate(&template); err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to create template: %v", err)})
 		return
 	}
 
-	// Implementation for creating template
 	c.JSON(http.StatusCreated, template)
 }
 
@@ -51,8 +67,17 @@ func (h *TemplateHandler) CreateTemplate(c *gin.Context) {
 func (h *TemplateHandler) GetTemplate(c *gin.Context) {
 	id := c.Param("id")
 
-	// Implementation for getting a template
-	template := models.Template{ID: id}
+	template, err := h.service.GetTemplate(id)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to fetch template: %v", err)})
+		return
+	}
+
+	if template == nil {
+		c.JSON(http.StatusNotFound, gin.H{"error": "Template not found"})
+		return
+	}
+
 	c.JSON(http.StatusOK, template)
 }
 
@@ -60,49 +85,68 @@ func (h *TemplateHandler) GetTemplate(c *gin.Context) {
 func (h *TemplateHandler) UpdateTemplate(c *gin.Context) {
 	id := c.Param("id")
 
-	var template models.Template
-	if err := c.ShouldBindJSON(&template); err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
+	var updatedTemplate models.Template
+	if err := c.ShouldBindJSON(&updatedTemplate); err != nil {
+		c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid request body: %v", err)})
 		return
 	}
 
-	template.ID = id
-	// Implementation for updating template
+	// Ensure the ID matches the URL parameter
+	updatedTemplate.ID = id
 
-	c.JSON(http.StatusOK, template)
+	if err := h.service.UpdateTemplate(&updatedTemplate); err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to update template: %v", err)})
+		return
+	}
+
+	c.JSON(http.StatusOK, updatedTemplate)
 }
 
 // DeleteTemplate deletes a template
 func (h *TemplateHandler) DeleteTemplate(c *gin.Context) {
 	id := c.Param("id")
 
-	fmt.Println("Deleting template with ID:", id)
-
-	// Implementation for deleting template
+	if err := h.service.DeleteTemplate(id); err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to delete template: %v", err)})
+		return
+	}
 
 	c.Status(http.StatusNoContent)
 }
 
-// DeployTemplate deploys a template
-func (h *TemplateHandler) DeployTemplate(c *gin.Context) {
+// GetTemplateDeployments returns all deployments for a template
+func (h *TemplateHandler) GetTemplateDeployments(c *gin.Context) {
 	id := c.Param("id")
 
-	var deployRequest struct {
-		Name string `json:"name"`
-		// Other deployment parameters
+	deployments, err := h.service.GetTemplateDeployments(id)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to fetch template deployments: %v", err)})
+		return
+	}
+
+	c.JSON(http.StatusOK, deployments)
+}
+
+// GetTemplateByVersion handles retrieval of a template by name and version
+func (h *TemplateHandler) GetTemplateByVersion(c *gin.Context) {
+	name := c.Query("name")
+	version := c.Query("version")
+
+	if name == "" || version == "" {
+		c.JSON(http.StatusBadRequest, gin.H{"error": "Both name and version parameters are required"})
+		return
 	}
 
-	if err := c.ShouldBindJSON(&deployRequest); err != nil {
-		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
+	template, err := h.service.GetTemplateByVersion(name, version)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to fetch template: %v", err)})
 		return
 	}
 
-	// Implementation for deploying template
-	result := map[string]interface{}{
-		"template_id":   id,
-		"deployment_id": "new-deployment-id",
-		"status":        "deploying",
+	if template == nil {
+		c.JSON(http.StatusNotFound, gin.H{"error": "Template not found"})
+		return
 	}
 
-	c.JSON(http.StatusAccepted, result)
+	c.JSON(http.StatusOK, template)
 }

+ 11 - 0
middleware/auth.go

@@ -9,6 +9,9 @@ import (
 	"github.com/golang-jwt/jwt/v5"
 )
 
+// debugMode is a flag to enable or disable debug logging
+var debug = true
+
 // JWT secret key - in production, this should be loaded from environment variables
 var jwtSecret = []byte("your-secret-key-here")
 
@@ -21,6 +24,14 @@ type Claims struct {
 
 // Auth middleware that accepts the auth service as dependency
 func Auth(authService auth.Service) gin.HandlerFunc {
+	if debug {
+		return func(c *gin.Context) {
+			c.Set("clientID", "debug_user")
+			c.Set("user_id", "debug_user")
+			c.Set("role", "admin")
+			c.Next()
+		}
+	}
 	return func(c *gin.Context) {
 		// Get token from request
 		token := extractTokenFromHeader(c)

+ 105 - 0
models/apps.go

@@ -0,0 +1,105 @@
+package models
+
+import (
+	"encoding/json"
+	"time"
+
+	"gorm.io/gorm"
+)
+
+type App struct {
+	ID          string `json:"id" gorm:"primaryKey"`
+	Name        string `json:"name" gorm:"not null"`
+	Description string `json:"description"`
+	Type        string `json:"type" gorm:"index"` // frontend, backend, api, database, or microservice
+	Language    string `json:"language"`          // Programming language or framework
+	Version     string `json:"version"`           // Version number (e.g., 1.0.0)
+
+	// Configuration details
+	ConfigFile   string `json:"configFile" gorm:"type:text"`   // JSON configuration as a string
+	EnvVariables string `json:"envVariables" gorm:"type:text"` // Environment variables as a string
+
+	// Source code details
+	Repository   string `json:"repository"`                   // Git repository URL
+	Branch       string `json:"branch" gorm:"default:'main'"` // Git branch (default: main)
+	BuildCommand string `json:"buildCommand"`                 // Command to build the app
+
+	// Resource allocation - stored as JSON
+	ResourcesJSON     string `json:"-" gorm:"column:resources;type:text"`
+	ScaleSettingsJSON string `json:"-" gorm:"column:scale_settings;type:text"`
+
+	// Virtual fields for ORM serialization/deserialization
+	Resources     ResourceRequirements `json:"resources" gorm:"-"`
+	ScaleSettings ScaleSettings        `json:"scaleSettings" gorm:"-"`
+
+	CreatedAt time.Time      `json:"createdAt" gorm:"autoCreateTime"`
+	UpdatedAt time.Time      `json:"updatedAt" gorm:"autoUpdateTime"`
+	CreatedBy string         `json:"createdBy" gorm:"index"` // User ID who created the app
+	DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`         // Soft delete support
+
+	// Relationships
+	Deployments []DeployedApp `json:"deployments" gorm:"foreignKey:AppID"` // Apps deployed in deployments
+}
+
+type ResourceRequirements struct {
+	CPU     string `json:"cpu"`     // e.g., "0.5"
+	Memory  string `json:"memory"`  // e.g., "512Mi"
+	Storage string `json:"storage"` // e.g., "1Gi"
+}
+
+type ScaleSettings struct {
+	MinInstances int `json:"minInstances"` // Minimum number of instances
+	MaxInstances int `json:"maxInstances"` // Maximum number of instances
+	CPUThreshold int `json:"cpuThreshold"` // CPU threshold for scaling (percentage)
+}
+
+// AppType represents the type of application
+type AppType string
+
+const (
+	Frontend     AppType = "frontend"
+	Backend      AppType = "backend"
+	API          AppType = "api"
+	Database     AppType = "database"
+	Microservice AppType = "microservice"
+)
+
+// BeforeSave serializes the embedded JSON fields
+func (a *App) BeforeSave(tx *gorm.DB) error {
+	var err error
+
+	// Marshal Resources to JSON
+	resourcesJSON, err := json.Marshal(a.Resources)
+	if err != nil {
+		return err
+	}
+	a.ResourcesJSON = string(resourcesJSON)
+
+	// Marshal ScaleSettings to JSON
+	scaleSettingsJSON, err := json.Marshal(a.ScaleSettings)
+	if err != nil {
+		return err
+	}
+	a.ScaleSettingsJSON = string(scaleSettingsJSON)
+
+	return nil
+}
+
+// AfterFind deserializes the JSON fields
+func (a *App) AfterFind(tx *gorm.DB) error {
+	// Unmarshal Resources from JSON
+	if a.ResourcesJSON != "" {
+		if err := json.Unmarshal([]byte(a.ResourcesJSON), &a.Resources); err != nil {
+			return err
+		}
+	}
+
+	// Unmarshal ScaleSettings from JSON
+	if a.ScaleSettingsJSON != "" {
+		if err := json.Unmarshal([]byte(a.ScaleSettingsJSON), &a.ScaleSettings); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

+ 0 - 49
models/autodeploy.go

@@ -1,49 +0,0 @@
-package models
-
-import (
-	"encoding/json"
-	"time"
-)
-
-// AutoDeploySettings contains auto-deployment configuration
-type AutoDeploySettings struct {
-	Enabled               bool              `json:"enabled"`
-	DefaultProviderID     string            `json:"default_provider_id"`
-	DefaultTemplateID     string            `json:"default_template_id"`
-	DefaultRegion         string            `json:"default_region"`
-	AutoDeployNewClients  bool              `json:"auto_deploy_new_clients"`
-	WebhookSecret         string            `json:"webhook_secret,omitempty"`
-	WebhookURL            string            `json:"webhook_url"`
-	DefaultTags           map[string]string `json:"default_tags"`
-	ResourceLimits        ResourceLimits    `json:"resource_limits"`
-	NotificationEndpoints []string          `json:"notification_endpoints"`
-}
-
-// ResourceLimits defines resource constraints for auto-deployments
-type ResourceLimits struct {
-	MaxCPUCores int `json:"max_cpu_cores"`
-	MaxMemoryGB int `json:"max_memory_gb"`
-	MaxDiskGB   int `json:"max_disk_gb"`
-	MaxCount    int `json:"max_count"` // Maximum number of instances
-}
-
-// AutoDeployRequest represents a request to automatically deploy for a client
-type AutoDeployRequest struct {
-	ClientID   string            `json:"client_id"`
-	TemplateID string            `json:"template_id,omitempty"`
-	ProviderID string            `json:"provider_id,omitempty"`
-	Region     string            `json:"region,omitempty"`
-	Tags       map[string]string `json:"tags,omitempty"`
-	Priority   string            `json:"priority,omitempty"` // Low, Medium, High
-}
-
-// WebhookPayload represents data sent to or received from a webhook
-type WebhookPayload struct {
-	Event      string          `json:"event"`
-	Timestamp  time.Time       `json:"timestamp"`
-	ClientID   string          `json:"client_id"`
-	Data       json.RawMessage `json:"data"`
-	Signature  string          `json:"signature,omitempty"`
-	RequestID  string          `json:"request_id"`
-	ProviderID string          `json:"provider_id,omitempty"`
-}

+ 21 - 13
models/client.go

@@ -2,22 +2,30 @@ package models
 
 import (
 	"time"
+
+	"gorm.io/gorm"
 )
 
 type Client struct {
-	ID string `json:"id"`
-	// TODO: Add Client fields
-	Name      string    `json:"name"`
-	CreatedAt time.Time `json:"created_at"`
-	UpdatedAt time.Time `json:"updated_at"`
-}
+	ID           string         `json:"id" gorm:"primaryKey"`            // Unique identifier
+	Name         string         `json:"name" gorm:"not null"`            // Client name
+	ContactEmail string         `json:"contactEmail" gorm:"index"`       // Client contact email
+	ContactPhone string         `json:"contactPhone,omitempty"`          // Optional contact phone
+	Organization string         `json:"organization"`                    // Client organization name
+	Plan         PlanType       `json:"plan" gorm:"default:'basic'"`     // Client plan type (basic, pro, enterprise)
+	CreatedAt    time.Time      `json:"createdAt" gorm:"autoCreateTime"` // Creation timestamp
+	UpdatedAt    time.Time      `json:"updatedAt" gorm:"autoUpdateTime"` // Last update timestamp
+	DeletedAt    gorm.DeletedAt `json:"deletedAt" gorm:"index"`          // Soft delete support
 
-// GetID returns the user's ID
-func (c *Client) GetID() string {
-	return c.ID
+	// GORM relationships
+	Deployments []Deployment `json:"deployments" gorm:"foreignKey:ClientID"` // Deployments belonging to this client
 }
 
-// SetID sets the user's ID
-func (c *Client) SetID(id string) {
-	c.ID = id
-}
+// PlanType represents the type of plan a client can have
+type PlanType string
+
+const (
+	Basic      PlanType = "basic"      // Basic plan
+	Pro        PlanType = "pro"        // Pro plan
+	Enterprise PlanType = "enterprise" // Enterprise plan
+)

+ 135 - 29
models/deployment.go

@@ -2,40 +2,146 @@ package models
 
 import (
 	"time"
+
+	"gorm.io/gorm"
 )
 
+// Deployment represents a deployed instance of an application
 type Deployment struct {
-	ID            string    `json:"id"`
-	ClientID      string    `json:"client_id"`
-	ProviderID    string    `json:"provider_id"`
-	TemplateID    string    `json:"template_id"`
-	Status        string    `json:"status"` // Pending, Running, Failed, Terminated
-	Region        string    `json:"region"`
-	VPSID         string    `json:"vps_id"` // ID assigned by provider
-	IPAddress     string    `json:"ip_address"`
-	Configuration string    `json:"configuration"` // JSON configuration
-	CreatedAt     time.Time `json:"created_at"`
-	UpdatedAt     time.Time `json:"updated_at"`
-	DeployedAt    time.Time `json:"deployed_at,omitempty"`
-	TerminatedAt  time.Time `json:"terminated_at,omitempty"`
+	ID          string `json:"id" gorm:"primaryKey"` // Unique identifier
+	Name        string `json:"name" gorm:"not null"` // Deployment name
+	Description string `json:"description"`          // Deployment description
+
+	// Core relationships
+	TemplateID string `json:"templateId" gorm:"index"` // Reference to the template being deployed
+	ClientID   string `json:"clientId" gorm:"index"`   // Client this deployment belongs to
+
+	// Status and environment
+	Status      string `json:"status" gorm:"default:'pending'"`          // Current deployment status
+	Environment string `json:"environment" gorm:"default:'development'"` // dev, staging, production
+	Region      string `json:"region"`                                   // Geographic region
+
+	// Deployment configuration
+	Hostname     string `json:"hostname"`     // External hostname for the deployment
+	CustomDomain string `json:"customDomain"` // Custom domain if configured
+
+	// Operational data
+	LogsConfig    string `json:"logsConfig" gorm:"type:text"`    // Logging configuration as JSON
+	MetricsConfig string `json:"metricsConfig" gorm:"type:text"` // Metrics configuration as JSON
+	AlertsConfig  string `json:"alertsConfig" gorm:"type:text"`  // Alert configurations as JSON
+
+	CreatedAt      time.Time      `json:"createdAt" gorm:"autoCreateTime"` // Creation timestamp
+	UpdatedAt      time.Time      `json:"updatedAt" gorm:"autoUpdateTime"` // Last update timestamp
+	LastDeployedAt time.Time      `json:"lastDeployedAt"`                  // When the deployment was last deployed
+	CreatedBy      string         `json:"createdBy" gorm:"index"`          // User ID who created the deployment
+	DeletedAt      gorm.DeletedAt `json:"-" gorm:"index"`                  // Soft delete support
+
+	// GORM relationships
+	DeployedApps []DeployedApp `json:"deployedApps" gorm:"foreignKey:DeploymentID"` // Array of deployed applications
+}
+
+// DeployedApp represents a specific app within a deployment
+type DeployedApp struct {
+	ID             string         `json:"id" gorm:"primaryKey"`                  // Unique identifier
+	DeploymentID   string         `json:"deploymentId" gorm:"index"`             // Reference to the parent deployment
+	AppID          string         `json:"appId" gorm:"index"`                    // Reference to the app being deployed
+	Status         string         `json:"status" gorm:"default:'pending'"`       // Status of this specific app's deployment
+	Version        string         `json:"version"`                               // Deployed version
+	URL            string         `json:"url"`                                   // URL to access this app
+	PodCount       int            `json:"podCount" gorm:"default:1"`             // Number of running instances/pods
+	HealthStatus   string         `json:"healthStatus" gorm:"default:'pending'"` // Current health status
+	ConfigSnapshot string         `json:"configSnapshot" gorm:"type:text"`       // Snapshot of configuration at deployment time
+	CreatedAt      time.Time      `json:"createdAt" gorm:"autoCreateTime"`       // Creation timestamp
+	UpdatedAt      time.Time      `json:"updatedAt" gorm:"autoUpdateTime"`       // Last update timestamp
+	DeletedAt      gorm.DeletedAt `json:"-" gorm:"index"`                        // Soft delete support
+
+	// GORM relationships - these will be serialized/deserialized as JSON
+	Resources ResourceAllocation `json:"resources" gorm:"-"` // Actual resources allocated
+}
+
+// App resource allocation (will be stored in DeployedAppResource table)
+type DeployedAppResource struct {
+	ID            string    `json:"id" gorm:"primaryKey"`              // Unique identifier
+	DeployedAppID string    `json:"deployedAppId" gorm:"uniqueIndex"`  // Reference to deployed app
+	CPU           string    `json:"cpu"`                               // Allocated CPU
+	CPUUsage      float64   `json:"cpuUsage"`                          // Current CPU usage percentage
+	Memory        string    `json:"memory"`                            // Allocated memory
+	MemoryUsage   float64   `json:"memoryUsage"`                       // Current memory usage percentage
+	Storage       string    `json:"storage"`                           // Allocated storage
+	StorageUsage  float64   `json:"storageUsage"`                      // Current storage usage percentage
+	LastUpdated   time.Time `json:"lastUpdated" gorm:"autoUpdateTime"` // When metrics were last updated
 }
 
-type AutoDeployConfig struct {
-	ID        string    `json:"id"`
-	ProjectID string    `json:"project_id"`
-	Branch    string    `json:"branch"`
-	Trigger   string    `json:"trigger"`
-	Enabled   bool      `json:"enabled"`
-	CreatedAt time.Time `json:"created_at"`
-	UpdatedAt time.Time `json:"updated_at"`
+// For backward compatibility
+type ResourceAllocation struct {
+	CPU          string  `json:"cpu"`          // Allocated CPU
+	CPUUsage     float64 `json:"cpuUsage"`     // Current CPU usage percentage
+	Memory       string  `json:"memory"`       // Allocated memory
+	MemoryUsage  float64 `json:"memoryUsage"`  // Current memory usage percentage
+	Storage      string  `json:"storage"`      // Allocated storage
+	StorageUsage float64 `json:"storageUsage"` // Current storage usage percentage
 }
 
-type AutoDeployHistory struct {
-	ID        string    `json:"id"`
-	ProjectID string    `json:"project_id"`
-	Branch    string    `json:"branch"`
-	Trigger   string    `json:"trigger"`
-	Status    string    `json:"status"`
-	CreatedAt time.Time `json:"created_at"`
-	UpdatedAt time.Time `json:"updated_at"`
+// LogConfiguration, MetricsConfiguration, and AlertConfiguration remain the same
+// These will be serialized/deserialized as JSON
+
+type LogConfiguration struct {
+	Enabled       bool   `json:"enabled"`       // Whether logging is enabled
+	RetentionDays int    `json:"retentionDays"` // Number of days to retain logs
+	ExternalSink  string `json:"externalSink"`  // External logging system URL if any
+}
+
+type MetricsConfiguration struct {
+	Enabled       bool     `json:"enabled"`       // Whether metrics collection is enabled
+	RetentionDays int      `json:"retentionDays"` // Number of days to retain metrics
+	CustomMetrics []string `json:"customMetrics"` // Any custom metrics to collect
 }
+
+type AlertConfiguration struct {
+	Type                 string   `json:"type"`                 // Type of alert
+	Threshold            float64  `json:"threshold"`            // Threshold value
+	Operator             string   `json:"operator"`             // ">", "<", ">=", "<=", "=="
+	Duration             string   `json:"duration"`             // How long condition must be true before alerting
+	NotificationChannels []string `json:"notificationChannels"` // Channels to notify (email, slack, etc.)
+}
+
+// DeploymentStatus type definitions
+type DeploymentStatus string
+type Environment string
+type AppDeploymentStatus string
+type HealthStatus string
+type AlertType string
+
+const (
+	// DeploymentStatus values
+	PENDING_DEPLOYMENT  DeploymentStatus = "pending"
+	DEPLOYING           DeploymentStatus = "deploying"
+	DEPLOYED            DeploymentStatus = "deployed"
+	FAILED_DEPLOYMENT   DeploymentStatus = "failed"
+	UPDATING_DEPLOYMENT DeploymentStatus = "updating"
+	DELETING            DeploymentStatus = "deleting"
+
+	// Environment values
+	DEVELOPMENT Environment = "development"
+	STAGING     Environment = "staging"
+	PRODUCTION  Environment = "production"
+
+	// AppDeploymentStatus values
+	PENDING_APP  AppDeploymentStatus = "pending"
+	RUNNING      AppDeploymentStatus = "running"
+	FAILED_APP   AppDeploymentStatus = "failed"
+	SCALING      AppDeploymentStatus = "scaling"
+	UPDATING_APP AppDeploymentStatus = "updating"
+
+	// HealthStatus values
+	HEALTHY   HealthStatus = "healthy"
+	DEGRADED  HealthStatus = "degraded"
+	UNHEALTHY HealthStatus = "unhealthy"
+
+	// AlertType values
+	CPU_USAGE    AlertType = "cpu_usage"
+	MEMORY_USAGE AlertType = "memory_usage"
+	DISK_USAGE   AlertType = "disk_usage"
+	ERROR_RATE   AlertType = "error_rate"
+	LATENCY      AlertType = "latency"
+)

+ 91 - 13
models/template.go

@@ -1,21 +1,99 @@
 package models
 
 import (
+	"encoding/json"
 	"time"
+
+	"gorm.io/gorm"
 )
 
 type Template struct {
-	ID                   string `json:"id"`
-	Name                 string `json:"name"`
-	Description          string `json:"description"`
-	Dockerfile           string `json:"dockerfile"`
-	DockerCompose        string `json:"docker_compose"`
-	ConfigTemplate       string `json:"config_template"` // For generating client-specific configs
-	ResourceRequirements struct {
-		CPU      int `json:"cpu"`
-		MemoryMB int `json:"memory_mb"`
-		DiskGB   int `json:"disk_gb"`
-	} `json:"resource_requirements"`
-	CreatedAt time.Time `json:"created_at"`
-	UpdatedAt time.Time `json:"updated_at"`
+	ID          string `json:"id" gorm:"primaryKey"`
+	Name        string `json:"name" gorm:"not null"`
+	Description string `json:"description"`
+	Version     string `json:"version" gorm:"index"`
+
+	// Configuration as JSON string in DB
+	ConfigJSON string `json:"-" gorm:"column:config;type:text"`
+
+	// Virtual field for ORM serialization/deserialization
+	Config TemplateConfig `json:"config" gorm:"-"`
+
+	CreatedAt time.Time      `json:"createdAt" gorm:"autoCreateTime"`
+	UpdatedAt time.Time      `json:"updatedAt" gorm:"autoUpdateTime"`
+	CreatedBy string         `json:"createdBy" gorm:"index"` // User ID who created the template
+	DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`         // Soft delete support
+
+	// Relationships
+	Deployments []Deployment `json:"deployments" gorm:"foreignKey:TemplateID"` // Deployments using this template
+}
+
+type TemplateConfig struct {
+	Apps            []AppConfig       `json:"apps"`                   // Apps included in this template
+	NetworkPolicies []NetworkPolicy   `json:"networkPolicies"`        // Network policies to apply
+	EnvVariables    map[string]string `json:"envVariables,omitempty"` // Environment variables
+	Secrets         []SecretConfig    `json:"secrets,omitempty"`      // Secret configurations
+}
+
+type AppConfig struct {
+	ID           string            `json:"id"`                     // Reference to the app
+	Name         string            `json:"name"`                   // Name of the app in this template
+	ExposedPorts []int             `json:"exposedPorts,omitempty"` // Ports to expose
+	PublicAccess bool              `json:"publicAccess"`           // Whether the app is publicly accessible
+	Resources    ResourceConfig    `json:"resources"`              // Resource allocation
+	Autoscaling  AutoscalingConfig `json:"autoscaling,omitempty"`  // Autoscaling configuration
+	EnvOverrides map[string]string `json:"envOverrides,omitempty"` // Environment variable overrides
+	ServiceMesh  bool              `json:"serviceMesh"`            // Whether to include in service mesh
+}
+
+type ResourceConfig struct {
+	CPU     string `json:"cpu"`     // e.g., "0.5"
+	Memory  string `json:"memory"`  // e.g., "512Mi"
+	Storage string `json:"storage"` // e.g., "1Gi"
+}
+
+type AutoscalingConfig struct {
+	Enabled      bool   `json:"enabled"`      // Whether autoscaling is enabled
+	MinReplicas  int    `json:"minReplicas"`  // Minimum number of replicas
+	MaxReplicas  int    `json:"maxReplicas"`  // Maximum number of replicas
+	CPUThreshold int    `json:"cpuThreshold"` // CPU threshold for scaling (percentage)
+	Metric       string `json:"metric"`       // Metric to base scaling on (e.g., "cpu", "memory")
+}
+
+type NetworkPolicy struct {
+	Name        string   `json:"name"`            // Policy name
+	FromApps    []string `json:"fromApps"`        // Source apps
+	ToApps      []string `json:"toApps"`          // Destination apps
+	Ports       []int    `json:"ports,omitempty"` // Allowed ports
+	AllowEgress bool     `json:"allowEgress"`     // Whether to allow egress traffic
+}
+
+type SecretConfig struct {
+	Name        string `json:"name"`        // Secret name
+	Description string `json:"description"` // Secret description
+	Required    bool   `json:"required"`    // Whether the secret is required
+}
+
+// BeforeSave serializes the embedded JSON fields
+func (t *Template) BeforeSave(tx *gorm.DB) error {
+	// Marshal Config to JSON
+	configJSON, err := json.Marshal(t.Config)
+	if err != nil {
+		return err
+	}
+	t.ConfigJSON = string(configJSON)
+
+	return nil
+}
+
+// AfterFind deserializes the JSON fields
+func (t *Template) AfterFind(tx *gorm.DB) error {
+	// Unmarshal Config from JSON
+	if t.ConfigJSON != "" {
+		if err := json.Unmarshal([]byte(t.ConfigJSON), &t.Config); err != nil {
+			return err
+		}
+	}
+
+	return nil
 }

+ 65 - 11
models/user.go

@@ -1,20 +1,74 @@
 package models
 
+import (
+	"encoding/json"
+	"time"
+
+	"gorm.io/gorm"
+
+	"golang.org/x/crypto/bcrypt"
+)
+
 // User represents a user in the system
 type User struct {
-	ID       string `json:"id"`
-	Username string `json:"username" binding:"required"`
-	Email    string `json:"email" binding:"required,email"`
-	Password string `json:"password" binding:"required,min=6"`
-	// Other user fields
+	ID              string          `json:"id" gorm:"primaryKey"`                  // Unique identifier
+	Username        string          `json:"username" gorm:"uniqueIndex;not null"`  // Username
+	Email           string          `json:"email" gorm:"uniqueIndex;not null"`     // User's email address
+	Password        string          `json:"password,omitempty" gorm:"not null"`    // Password (hashed)
+	Role            string          `json:"role" gorm:"default:'user'"`            // User role
+	PreferencesJSON string          `json:"-" gorm:"column:preferences;type:text"` // User preferences stored as JSON string
+	Preferences     UserPreferences `json:"preferences" gorm:"-"`                  // User preferences (transient field)
+	CreatedAt       time.Time       `json:"createdAt" gorm:"autoCreateTime"`       // Creation timestamp
+	UpdatedAt       time.Time       `json:"updatedAt" gorm:"autoUpdateTime"`       // Last update timestamp
+	DeletedAt       gorm.DeletedAt  `json:"-" gorm:"index"`                        // Soft delete support
 }
 
-// GetID returns the user's ID
-func (u *User) GetID() string {
-	return u.ID
+// UserPreferences represents user-specific settings
+type UserPreferences struct {
+	Theme           string      `json:"theme"`                     // User's theme preference
+	Notifications   bool        `json:"notifications"`             // Notification preference
+	DashboardLayout interface{} `json:"dashboardLayout,omitempty"` // Optional dashboard layout
 }
 
-// SetID sets the user's ID
-func (u *User) SetID(id string) {
-	u.ID = id
+// BeforeSave hook runs before saving a User - handles password hashing and preferences serialization
+func (u *User) BeforeSave(tx *gorm.DB) error {
+	// Only hash password if it's provided and not already hashed
+	if u.Password != "" && len(u.Password) < 60 { // bcrypt hashes are typically 60 characters
+		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
+		if err != nil {
+			return err
+		}
+		u.Password = string(hashedPassword)
+	}
+
+	// Serialize preferences to JSON
+	preferencesData, err := json.Marshal(u.Preferences)
+	if err != nil {
+		return err
+	}
+	u.PreferencesJSON = string(preferencesData)
+
+	return nil
 }
+
+// AfterFind hook deserializes JSON preferences after fetching from database
+func (u *User) AfterFind(tx *gorm.DB) error {
+	if u.PreferencesJSON != "" {
+		return json.Unmarshal([]byte(u.PreferencesJSON), &u.Preferences)
+	}
+	return nil
+}
+
+// CheckPassword verifies if the provided password matches the hashed one
+func (u *User) CheckPassword(password string) bool {
+	err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
+	return err == nil
+}
+
+type UserRole string
+
+const (
+	Admin     UserRole = "admin"
+	Developer UserRole = "developer"
+	Viewer    UserRole = "viewer"
+)

+ 118 - 0
services/apps.go

@@ -0,0 +1,118 @@
+package services
+
+import (
+	"fmt"
+
+	"git.linuxforward.com/byop/byop-engine/dbstore"
+	"git.linuxforward.com/byop/byop-engine/models"
+	"github.com/google/uuid"
+)
+
+// AppService handles business logic for applications
+type AppService struct {
+	store *dbstore.AppStore
+}
+
+// NewAppService creates a new AppService
+func NewAppService(store *dbstore.AppStore) *AppService {
+	return &AppService{store: store}
+}
+
+// CreateApp creates a new application
+func (s *AppService) CreateApp(app *models.App) error {
+	// Generate UUID if not provided
+	if app.ID == "" {
+		app.ID = uuid.New().String()
+	}
+
+	// Set default resource values if not provided
+	if app.Resources.CPU == "" {
+		app.Resources.CPU = "0.5"
+	}
+	if app.Resources.Memory == "" {
+		app.Resources.Memory = "512Mi"
+	}
+	if app.Resources.Storage == "" {
+		app.Resources.Storage = "1Gi"
+	}
+
+	// Set default scale settings if not provided
+	if app.ScaleSettings.MinInstances == 0 {
+		app.ScaleSettings.MinInstances = 1
+	}
+	if app.ScaleSettings.MaxInstances == 0 {
+		app.ScaleSettings.MaxInstances = 3
+	}
+	if app.ScaleSettings.CPUThreshold == 0 {
+		app.ScaleSettings.CPUThreshold = 80
+	}
+
+	// Persist the app
+	return s.store.Create(app)
+}
+
+// GetApp retrieves an application by ID
+func (s *AppService) GetApp(id string) (*models.App, error) {
+	app, err := s.store.GetByID(id)
+	if err != nil {
+		return nil, fmt.Errorf("failed to retrieve app: %w", err)
+	}
+	return app, nil
+}
+
+// UpdateApp updates an existing application
+func (s *AppService) UpdateApp(app *models.App) error {
+	if app.ID == "" {
+		return fmt.Errorf("app ID is required for update")
+	}
+
+	// Check if app exists
+	existingApp, err := s.store.GetByID(app.ID)
+	if err != nil {
+		return fmt.Errorf("failed to check if app exists: %w", err)
+	}
+	if existingApp == nil {
+		return fmt.Errorf("app with ID %s not found", app.ID)
+	}
+
+	return s.store.Update(app)
+}
+
+// DeleteApp deletes an application by ID
+func (s *AppService) DeleteApp(id string) error {
+	// Check if app exists
+	app, err := s.store.GetByID(id)
+	if err != nil {
+		return fmt.Errorf("failed to check if app exists: %w", err)
+	}
+	if app == nil {
+		return fmt.Errorf("app with ID %s not found", id)
+	}
+
+	return s.store.Delete(id)
+}
+
+// ListApps retrieves all applications with optional filtering
+func (s *AppService) ListApps(filter map[string]interface{}) ([]*models.App, error) {
+	return s.store.List(filter)
+}
+
+// GetAppDeployments retrieves all deployments for an application
+func (s *AppService) GetAppDeployments(id string) ([]models.DeployedApp, error) {
+	// First check if the app exists
+	app, err := s.store.GetByID(id)
+	if err != nil {
+		return nil, fmt.Errorf("failed to check if app exists: %w", err)
+	}
+	if app == nil {
+		return nil, fmt.Errorf("app with ID %s not found", id)
+	}
+
+	// Get app with deployments
+	appWithDeployments, err := s.store.GetAppWithDeployments(id)
+	if err != nil {
+		return nil, fmt.Errorf("failed to retrieve app deployments: %w", err)
+	}
+
+	return appWithDeployments.Deployments, nil
+}

+ 0 - 3
services/autodeploy.go

@@ -1,3 +0,0 @@
-package services
-
-// TODO: Implement autodeploy service

+ 430 - 1
services/deployments.go

@@ -1,3 +1,432 @@
 package services
 
-// TODO: Implement deployments service
+import (
+	"encoding/json"
+	"fmt"
+	"time"
+
+	"git.linuxforward.com/byop/byop-engine/dbstore"
+	"git.linuxforward.com/byop/byop-engine/models"
+	"github.com/google/uuid"
+)
+
+// DeploymentService handles business logic for deployments
+type DeploymentService struct {
+	store         *dbstore.DeploymentStore
+	appStore      *dbstore.AppStore
+	templateStore *dbstore.TemplateStore
+	clientStore   *dbstore.ClientStore
+}
+
+// NewDeploymentService creates a new DeploymentService
+func NewDeploymentService(
+	store *dbstore.DeploymentStore,
+	appStore *dbstore.AppStore,
+	templateStore *dbstore.TemplateStore,
+	clientStore *dbstore.ClientStore,
+) *DeploymentService {
+	return &DeploymentService{
+		store:         store,
+		appStore:      appStore,
+		templateStore: templateStore,
+		clientStore:   clientStore,
+	}
+}
+
+// CreateDeployment creates a new deployment
+func (s *DeploymentService) CreateDeployment(deployment *models.Deployment) error {
+	// Generate UUID if not provided
+	if deployment.ID == "" {
+		deployment.ID = uuid.New().String()
+	}
+
+	// Validate the deployment
+	if err := s.validateDeployment(deployment); err != nil {
+		return fmt.Errorf("invalid deployment: %w", err)
+	}
+
+	// Set appropriate status
+	deployment.Status = string(models.PENDING_DEPLOYMENT)
+
+	// Set timestamps
+	now := time.Now()
+	deployment.LastDeployedAt = now
+
+	// Handle deployed apps setup
+	if err := s.setupDeployedApps(deployment); err != nil {
+		return fmt.Errorf("failed to setup deployed apps: %w", err)
+	}
+
+	// Persist the deployment
+	if err := s.store.Create(deployment); err != nil {
+		return fmt.Errorf("failed to create deployment: %w", err)
+	}
+
+	// Trigger deployment process (this would normally be asynchronous)
+	// This is a placeholder for your actual deployment logic
+	go s.processDeployment(deployment.ID)
+
+	return nil
+}
+
+// GetDeployment retrieves a deployment by ID
+func (s *DeploymentService) GetDeployment(id string) (*models.Deployment, error) {
+	deployment, err := s.store.GetByID(id)
+	if err != nil {
+		return nil, fmt.Errorf("failed to retrieve deployment: %w", err)
+	}
+
+	if deployment != nil {
+		// Deserialize config fields
+		if err := s.deserializeConfigFields(deployment); err != nil {
+			return nil, fmt.Errorf("failed to deserialize config fields: %w", err)
+		}
+	}
+
+	return deployment, nil
+}
+
+// UpdateDeployment updates an existing deployment
+func (s *DeploymentService) UpdateDeployment(deployment *models.Deployment) error {
+	// Validate the deployment ID
+	if deployment.ID == "" {
+		return fmt.Errorf("deployment ID is required for update")
+	}
+
+	// Check if deployment exists
+	existingDeployment, err := s.store.GetByID(deployment.ID)
+	if err != nil {
+		return fmt.Errorf("failed to check if deployment exists: %w", err)
+	}
+	if existingDeployment == nil {
+		return fmt.Errorf("deployment with ID %s not found", deployment.ID)
+	}
+
+	// Prevent updates to deployed apps if deployment is not in the right state
+	if existingDeployment.Status != string(models.PENDING_DEPLOYMENT) &&
+		existingDeployment.Status != string(models.FAILED_DEPLOYMENT) &&
+		len(deployment.DeployedApps) > 0 {
+		return fmt.Errorf("cannot update deployed apps when deployment is in %s state", existingDeployment.Status)
+	}
+
+	// Validate the deployment
+	if err := s.validateDeployment(deployment); err != nil {
+		return fmt.Errorf("invalid deployment: %w", err)
+	}
+
+	// If status was updated to "deploying", update LastDeployedAt
+	if existingDeployment.Status != string(models.DEPLOYING) &&
+		deployment.Status == string(models.DEPLOYING) {
+		deployment.LastDeployedAt = time.Now()
+	}
+
+	// Handle deployed apps setup
+	if err := s.setupDeployedApps(deployment); err != nil {
+		return fmt.Errorf("failed to setup deployed apps: %w", err)
+	}
+
+	// Persist the deployment
+	if err := s.store.Update(deployment); err != nil {
+		return fmt.Errorf("failed to update deployment: %w", err)
+	}
+
+	return nil
+}
+
+// DeleteDeployment deletes a deployment by ID
+func (s *DeploymentService) DeleteDeployment(id string) error {
+	// Check if deployment exists
+	deployment, err := s.store.GetByID(id)
+	if err != nil {
+		return fmt.Errorf("failed to check if deployment exists: %w", err)
+	}
+	if deployment == nil {
+		return fmt.Errorf("deployment with ID %s not found", id)
+	}
+
+	// Set status to deleting
+	deployment.Status = string(models.DELETING)
+	if err := s.store.Update(deployment); err != nil {
+		return fmt.Errorf("failed to update deployment status: %w", err)
+	}
+
+	// Trigger cleanup process (this would normally be asynchronous)
+	// This is a placeholder for your actual cleanup logic
+	go s.processDeploymentCleanup(id)
+
+	return nil
+}
+
+// ListDeployments retrieves all deployments with optional filtering
+func (s *DeploymentService) ListDeployments(filter map[string]interface{}) ([]*models.Deployment, error) {
+	deployments, err := s.store.List(filter)
+	if err != nil {
+		return nil, fmt.Errorf("failed to list deployments: %w", err)
+	}
+
+	// Deserialize config fields for each deployment
+	for _, deployment := range deployments {
+		if err := s.deserializeConfigFields(deployment); err != nil {
+			return nil, fmt.Errorf("failed to deserialize config fields: %w", err)
+		}
+	}
+
+	return deployments, nil
+}
+
+// GetDeploymentsByClientID retrieves deployments for a specific client
+func (s *DeploymentService) GetDeploymentsByClientID(clientID string) ([]*models.Deployment, error) {
+	// Check if client exists
+	client, err := s.clientStore.GetByID(clientID)
+	if err != nil {
+		return nil, fmt.Errorf("failed to check if client exists: %w", err)
+	}
+	if client == nil {
+		return nil, fmt.Errorf("client with ID %s not found", clientID)
+	}
+
+	deployments, err := s.store.GetByClientID(clientID)
+	if err != nil {
+		return nil, fmt.Errorf("failed to retrieve deployments for client %s: %w", clientID, err)
+	}
+
+	// Deserialize config fields for each deployment
+	for _, deployment := range deployments {
+		if err := s.deserializeConfigFields(deployment); err != nil {
+			return nil, fmt.Errorf("failed to deserialize config fields: %w", err)
+		}
+	}
+
+	return deployments, nil
+}
+
+// GetDeploymentsByUserID retrieves deployments created by a specific user
+func (s *DeploymentService) GetDeploymentsByUserID(userID string) ([]*models.Deployment, error) {
+	deployments, err := s.store.GetByUserID(userID)
+	if err != nil {
+		return nil, fmt.Errorf("failed to retrieve deployments for user %s: %w", userID, err)
+	}
+
+	// Deserialize config fields for each deployment
+	for _, deployment := range deployments {
+		if err := s.deserializeConfigFields(deployment); err != nil {
+			return nil, fmt.Errorf("failed to deserialize config fields: %w", err)
+		}
+	}
+
+	return deployments, nil
+}
+
+// GetDeploymentsByTemplateID retrieves deployments based on a specific template
+func (s *DeploymentService) GetDeploymentsByTemplateID(templateID string) ([]*models.Deployment, error) {
+	// Check if template exists
+	template, err := s.templateStore.GetByID(templateID)
+	if err != nil {
+		return nil, fmt.Errorf("failed to check if template exists: %w", err)
+	}
+	if template == nil {
+		return nil, fmt.Errorf("template with ID %s not found", templateID)
+	}
+
+	deployments, err := s.store.GetByTemplateID(templateID)
+	if err != nil {
+		return nil, fmt.Errorf("failed to retrieve deployments for template %s: %w", templateID, err)
+	}
+
+	// Deserialize config fields for each deployment
+	for _, deployment := range deployments {
+		if err := s.deserializeConfigFields(deployment); err != nil {
+			return nil, fmt.Errorf("failed to deserialize config fields: %w", err)
+		}
+	}
+
+	return deployments, nil
+}
+
+// UpdateDeploymentStatus updates the status of a deployment
+func (s *DeploymentService) UpdateDeploymentStatus(id string, status string) error {
+	deployment, err := s.store.GetByID(id)
+	if err != nil {
+		return fmt.Errorf("failed to retrieve deployment: %w", err)
+	}
+	if deployment == nil {
+		return fmt.Errorf("deployment with ID %s not found", id)
+	}
+
+	// Update the status
+	deployment.Status = status
+
+	// If status is being set to "deploying", update LastDeployedAt
+	if status == string(models.DEPLOYING) {
+		deployment.LastDeployedAt = time.Now()
+	}
+
+	if err := s.store.Update(deployment); err != nil {
+		return fmt.Errorf("failed to update deployment status: %w", err)
+	}
+
+	return nil
+}
+
+// validateDeployment validates a deployment
+func (s *DeploymentService) validateDeployment(deployment *models.Deployment) error {
+	// Validate required fields
+	if deployment.Name == "" {
+		return fmt.Errorf("deployment name is required")
+	}
+
+	// Validate relationships
+	if deployment.ClientID == "" {
+		return fmt.Errorf("client ID is required")
+	}
+	client, err := s.clientStore.GetByID(deployment.ClientID)
+	if err != nil {
+		return fmt.Errorf("failed to check client: %w", err)
+	}
+	if client == nil {
+		return fmt.Errorf("client with ID %s not found", deployment.ClientID)
+	}
+
+	if deployment.TemplateID == "" {
+		return fmt.Errorf("template ID is required")
+	}
+	template, err := s.templateStore.GetByID(deployment.TemplateID)
+	if err != nil {
+		return fmt.Errorf("failed to check template: %w", err)
+	}
+	if template == nil {
+		return fmt.Errorf("template with ID %s not found", deployment.TemplateID)
+	}
+
+	return nil
+}
+
+// setupDeployedApps sets up deployed apps based on the template
+func (s *DeploymentService) setupDeployedApps(deployment *models.Deployment) error {
+	// If deployment already has deployed apps defined, we assume they're set up correctly
+	if len(deployment.DeployedApps) > 0 {
+		return nil
+	}
+
+	// Get the template
+	template, err := s.templateStore.GetByID(deployment.TemplateID)
+	if err != nil {
+		return fmt.Errorf("failed to retrieve template: %w", err)
+	}
+	if template == nil {
+		return fmt.Errorf("template with ID %s not found", deployment.TemplateID)
+	}
+
+	// Use the template config to set up deployed apps
+	var templateConfig models.TemplateConfig
+	if err := json.Unmarshal([]byte(template.ConfigJSON), &templateConfig); err != nil {
+		return fmt.Errorf("failed to parse template config: %w", err)
+	}
+
+	// Create deployed apps for each app in the template
+	for _, appConfig := range templateConfig.Apps {
+		// Get the app
+		app, err := s.appStore.GetByID(appConfig.ID)
+		if err != nil {
+			return fmt.Errorf("failed to retrieve app: %w", err)
+		}
+		if app == nil {
+			return fmt.Errorf("app with ID %s not found", appConfig.ID)
+		}
+
+		// Create a deployed app
+		deployedApp := models.DeployedApp{
+			ID:           uuid.New().String(),
+			DeploymentID: deployment.ID,
+			AppID:        app.ID,
+			Status:       string(models.PENDING_APP),
+			Version:      app.Version,
+			URL:          "", // Will be set during deployment
+			PodCount:     appConfig.Autoscaling.MinReplicas,
+			HealthStatus: string(models.HEALTHY),
+			Resources: models.ResourceAllocation{
+				CPU:     appConfig.Resources.CPU,
+				Memory:  appConfig.Resources.Memory,
+				Storage: appConfig.Resources.Storage,
+			},
+		}
+
+		// Add to deployment
+		deployment.DeployedApps = append(deployment.DeployedApps, deployedApp)
+	}
+
+	return nil
+}
+
+// processDeployment handles the actual deployment process
+func (s *DeploymentService) processDeployment(deploymentID string) {
+	// This would be an async process in a real system
+	// For now, we just update the status after a short delay to simulate the process
+
+	// Update status to deploying
+	_ = s.UpdateDeploymentStatus(deploymentID, string(models.DEPLOYING))
+
+	// In a real system, this would be where you'd:
+	// 1. Provision infrastructure
+	// 2. Deploy containers/apps
+	// 3. Configure networking
+	// 4. Setup monitoring
+	// etc.
+
+	// For this demo, we'll just update the status after a short delay
+	time.Sleep(2 * time.Second)
+
+	// Update status to deployed or failed (randomly for demonstration)
+	if time.Now().Unix()%2 == 0 { // Random success/failure
+		_ = s.UpdateDeploymentStatus(deploymentID, string(models.DEPLOYED))
+	} else {
+		_ = s.UpdateDeploymentStatus(deploymentID, string(models.FAILED_DEPLOYMENT))
+	}
+}
+
+// processDeploymentCleanup handles the cleanup process for deleted deployments
+func (s *DeploymentService) processDeploymentCleanup(deploymentID string) {
+	// This would be an async process in a real system
+	// In a real system, this would:
+	// 1. Deprovision infrastructure
+	// 2. Clean up resources
+	// 3. Remove configuration
+
+	// For this demo, we'll just delete after a short delay
+	time.Sleep(2 * time.Second)
+
+	// Delete the deployment from the database
+	_ = s.store.Delete(deploymentID)
+}
+
+// deserializeConfigFields deserializes JSON config fields from strings
+func (s *DeploymentService) deserializeConfigFields(deployment *models.Deployment) error {
+	// Deserialize logs config
+	if deployment.LogsConfig != "" {
+		var logsConfig models.LogConfiguration
+		if err := json.Unmarshal([]byte(deployment.LogsConfig), &logsConfig); err != nil {
+			return fmt.Errorf("failed to unmarshal logs config: %w", err)
+		}
+		// We could set this on the deployment if needed
+	}
+
+	// Deserialize metrics config
+	if deployment.MetricsConfig != "" {
+		var metricsConfig models.MetricsConfiguration
+		if err := json.Unmarshal([]byte(deployment.MetricsConfig), &metricsConfig); err != nil {
+			return fmt.Errorf("failed to unmarshal metrics config: %w", err)
+		}
+		// We could set this on the deployment if needed
+	}
+
+	// Deserialize alerts config
+	if deployment.AlertsConfig != "" {
+		var alertsConfig []models.AlertConfiguration
+		if err := json.Unmarshal([]byte(deployment.AlertsConfig), &alertsConfig); err != nil {
+			return fmt.Errorf("failed to unmarshal alerts config: %w", err)
+		}
+		// We could set this on the deployment if needed
+	}
+
+	return nil
+}

+ 0 - 3
services/monitoring.go

@@ -1,3 +0,0 @@
-package services
-
-// TODO: Implement monitoring service

+ 151 - 1
services/templates.go

@@ -1,3 +1,153 @@
 package services
 
-// TODO: Implement templates service
+import (
+	"fmt"
+
+	"git.linuxforward.com/byop/byop-engine/dbstore"
+	"git.linuxforward.com/byop/byop-engine/models"
+	"github.com/google/uuid"
+)
+
+// TemplateService handles business logic for templates
+type TemplateService struct {
+	store *dbstore.TemplateStore
+}
+
+// NewTemplateService creates a new TemplateService
+func NewTemplateService(store *dbstore.TemplateStore) *TemplateService {
+	return &TemplateService{store: store}
+}
+
+// CreateTemplate creates a new deployment template
+func (s *TemplateService) CreateTemplate(template *models.Template) error {
+	// Generate UUID if not provided
+	if template.ID == "" {
+		template.ID = uuid.New().String()
+	}
+
+	// Validate template configuration
+	if err := validateTemplateConfig(template.Config); err != nil {
+		return fmt.Errorf("invalid template configuration: %w", err)
+	}
+
+	// Persist the template
+	return s.store.Create(template)
+}
+
+// GetTemplate retrieves a template by ID
+func (s *TemplateService) GetTemplate(id string) (*models.Template, error) {
+	template, err := s.store.GetByID(id)
+	if err != nil {
+		return nil, fmt.Errorf("failed to retrieve template: %w", err)
+	}
+	return template, nil
+}
+
+// UpdateTemplate updates an existing template
+func (s *TemplateService) UpdateTemplate(template *models.Template) error {
+	if template.ID == "" {
+		return fmt.Errorf("template ID is required for update")
+	}
+
+	// Check if template exists
+	existingTemplate, err := s.store.GetByID(template.ID)
+	if err != nil {
+		return fmt.Errorf("failed to check if template exists: %w", err)
+	}
+	if existingTemplate == nil {
+		return fmt.Errorf("template with ID %s not found", template.ID)
+	}
+
+	// Validate template configuration
+	if err := validateTemplateConfig(template.Config); err != nil {
+		return fmt.Errorf("invalid template configuration: %w", err)
+	}
+
+	return s.store.Update(template)
+}
+
+// DeleteTemplate deletes a template by ID
+func (s *TemplateService) DeleteTemplate(id string) error {
+	// Check if template exists
+	template, err := s.store.GetByID(id)
+	if err != nil {
+		return fmt.Errorf("failed to check if template exists: %w", err)
+	}
+	if template == nil {
+		return fmt.Errorf("template with ID %s not found", id)
+	}
+
+	// Check if the template has deployments
+	templateWithDeployments, err := s.store.GetTemplateWithDeployments(id)
+	if err != nil {
+		return fmt.Errorf("failed to check template deployments: %w", err)
+	}
+
+	// Don't allow deletion if there are active deployments
+	if len(templateWithDeployments.Deployments) > 0 {
+		return fmt.Errorf("cannot delete template with active deployments")
+	}
+
+	return s.store.Delete(id)
+}
+
+// ListTemplates retrieves all templates with optional filtering
+func (s *TemplateService) ListTemplates(filter map[string]interface{}) ([]*models.Template, error) {
+	return s.store.List(filter)
+}
+
+// GetTemplateDeployments retrieves all deployments for a template
+func (s *TemplateService) GetTemplateDeployments(id string) ([]models.Deployment, error) {
+	// First check if the template exists
+	template, err := s.store.GetByID(id)
+	if err != nil {
+		return nil, fmt.Errorf("failed to check if template exists: %w", err)
+	}
+	if template == nil {
+		return nil, fmt.Errorf("template with ID %s not found", id)
+	}
+
+	// Get template with deployments
+	templateWithDeployments, err := s.store.GetTemplateWithDeployments(id)
+	if err != nil {
+		return nil, fmt.Errorf("failed to retrieve template deployments: %w", err)
+	}
+
+	return templateWithDeployments.Deployments, nil
+}
+
+// GetTemplateByVersion retrieves a template by name and version
+func (s *TemplateService) GetTemplateByVersion(name, version string) (*models.Template, error) {
+	template, err := s.store.GetByVersion(name, version)
+	if err != nil {
+		return nil, fmt.Errorf("failed to retrieve template: %w", err)
+	}
+	return template, nil
+}
+
+// validateTemplateConfig validates the template configuration
+func validateTemplateConfig(config models.TemplateConfig) error {
+	// Validate that at least one app is defined
+	if len(config.Apps) == 0 {
+		return fmt.Errorf("template must define at least one app")
+	}
+
+	// Validate each app in the template
+	for i, app := range config.Apps {
+		if app.Name == "" {
+			return fmt.Errorf("app at index %d must have a name", i)
+		}
+
+		// Validate resource configuration
+		if app.Resources.CPU == "" {
+			return fmt.Errorf("app '%s' must specify CPU resources", app.Name)
+		}
+		if app.Resources.Memory == "" {
+			return fmt.Errorf("app '%s' must specify memory resources", app.Name)
+		}
+	}
+
+	// Add additional validation logic as needed
+
+	return nil
+}

+ 5 - 0
vendor/github.com/bytedance/sonic/.codespellrc

@@ -0,0 +1,5 @@
+[codespell]
+# ignore test files, go project names, binary files via `skip` and special var/regex via `ignore-words`
+skip = fuzz,*_test.tmpl,testdata,*_test.go,go.mod,go.sum,*.gz
+ignore-words = .github/workflows/.ignore_words
+check-filenames = true

+ 4 - 1
vendor/github.com/bytedance/sonic/.gitignore

@@ -49,4 +49,7 @@ ast/bench.sh
 
 !testdata/*.json.gz
 fuzz/testdata
-*__debug_bin
+*__debug_bin*
+*pprof
+*coverage.txt
+tools/venv/*

+ 43 - 13
vendor/github.com/bytedance/sonic/README.md

@@ -6,9 +6,10 @@ A blazingly fast JSON serializing &amp; deserializing library, accelerated by JI
 
 ## Requirement
 
-- Go 1.16~1.22
-- Linux / MacOS / Windows(need go1.17 above)
-- Amd64 ARCH
+- Go: 1.17~1.24
+  - Notice: Go1.24.0 is not supported due to the [issue](https://github.com/golang/go/issues/71672), please use higher go version or add build tag `--ldflags="-checklinkname=0"` 
+- OS: Linux / MacOS / Windows
+- CPU: AMD64 / (ARM64, need go1.20 above)
 
 ## Features
 
@@ -211,7 +212,7 @@ ret, err := Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":"\u003c\u003e"
 
 ### Compact Format
 
-Sonic encodes primitive objects (struct/map...) as compact-format JSON by default, except marshaling `json.RawMessage` or `json.Marshaler`: sonic ensures validating their output JSON but **DONOT** compacting them for performance concerns. We provide the option `encoder.CompactMarshaler` to add compacting process.
+Sonic encodes primitive objects (struct/map...) as compact-format JSON by default, except marshaling `json.RawMessage` or `json.Marshaler`: sonic ensures validating their output JSON but **DO NOT** compacting them for performance concerns. We provide the option `encoder.CompactMarshaler` to add compacting process.
 
 ### Print Error
 
@@ -282,6 +283,22 @@ sub := root.Get("key3").Index(2).Int64() // == 3
 
 **Tip**: since `Index()` uses offset to locate data, which is much faster than scanning like `Get()`, we suggest you use it as much as possible. And sonic also provides another API `IndexOrGet()` to underlying use offset as well as ensure the key is matched.
 
+#### SearchOption
+
+`Searcher` provides some options for user to meet different needs:
+
+```go
+opts := ast.SearchOption{ CopyReturn: true ... }
+val, err := sonic.GetWithOptions(JSON, opts, "key")
+```
+
+- CopyReturn
+Indicate the searcher to copy the result JSON string instead of refer from the input. This can help to reduce memory usage if you cache the results
+- ConcurentRead
+Since `ast.Node` use `Lazy-Load` design, it doesn't support Concurrently-Read by default. If you want to read it concurrently, please specify it.
+- ValidateJSON
+Indicate the searcher to validate the entire JSON. This option is enabled by default, which slow down the search speed a little.
+
 #### Set/Unset
 
 Modify the json content by Set()/Unset()
@@ -368,16 +385,12 @@ See [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go
 
 ## Compatibility
 
-Sonic **DOES NOT** ensure to support all environments, due to the difficulty of developing high-performance codes. For developers who use sonic to build their applications in different environments, we have the following suggestions:
-
-- Developing on **Mac M1**: Make sure you have Rosetta 2 installed on your machine, and set `GOARCH=amd64` when building your application. Rosetta 2 can automatically translate x86 binaries to arm64 binaries and run x86 applications on Mac M1.
-- Developing on **Linux arm64**: You can install qemu and use the `qemu-x86_64 -cpu max` command to convert x86 binaries to amr64 binaries for applications built with sonic. The qemu can achieve a similar transfer effect to Rosetta 2 on Mac M1.
-
-For developers who want to use sonic on Linux arm64 without qemu, or those who want to handle JSON strictly consistent with `encoding/json`, we provide some compatible APIs as `sonic.API`
+For developers who want to use sonic to meet diffirent scenarios, we provide some integrated configs as `sonic.API`
 
-- `ConfigDefault`: the sonic's default config (`EscapeHTML=false`,`SortKeys=false`...) to run on sonic-supporting environment. It will fall back to `encoding/json` with the corresponding config, and some options like `SortKeys=false` will be invalid.
-- `ConfigStd`: the std-compatible config (`EscapeHTML=true`,`SortKeys=true`...) to run on sonic-supporting environment. It will fall back to `encoding/json`.
-- `ConfigFastest`: the fastest config (`NoQuoteTextMarshaler=true`) to run on sonic-supporting environment. It will fall back to `encoding/json` with the corresponding config, and some options will be invalid.
+- `ConfigDefault`: the sonic's default config (`EscapeHTML=false`,`SortKeys=false`...) to run sonic fast meanwhile ensure security.
+- `ConfigStd`: the std-compatible config (`EscapeHTML=true`,`SortKeys=true`...)
+- `ConfigFastest`: the fastest config (`NoQuoteTextMarshaler=true`) to run on sonic as fast as possible.
+Sonic **DOES NOT** ensure to support all environments, due to the difficulty of developing high-performance codes. On non-sonic-supporting environment, the implementation will fall back to `encoding/json`. Thus beflow configs will all equal to `ConfigStd`.
 
 ## Tips
 
@@ -466,6 +479,23 @@ For better performance, in previous case the `ast.Visitor` will be the better ch
 
 But `ast.Visitor` is not a very handy API. You might need to write a lot of code to implement your visitor and carefully maintain the tree hierarchy during decoding. Please read the comments in [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) carefully if you decide to use this API.
 
+### Buffer Size
+
+Sonic use memory pool in many places like `encoder.Encode`, `ast.Node.MarshalJSON` to improve performance, which may produce more memory usage (in-use) when server's load is high. See [issue 614](https://github.com/bytedance/sonic/issues/614). Therefore, we introduce some options to let user control the behavior of memory pool. See [option](https://pkg.go.dev/github.com/bytedance/sonic@v1.11.9/option#pkg-variables) package.
+
+### Faster JSON Skip
+
+For security, sonic use [FSM](native/skip_one.c) algorithm to  validate JSON when decoding raw JSON or encoding `json.Marshaler`, which is much slower (1~10x) than [SIMD-searching-pair](native/skip_one_fast.c) algorithm. If user has many redundant JSON value and DO NOT NEED to strictly validate JSON correctness, you can enable below options:
+
+- `Config.NoValidateSkipJSON`: for faster skipping JSON when decoding, such as unknown fields, json.Unmarshaler(json.RawMessage), mismatched values, and redundant array elements
+- `Config.NoValidateJSONMarshaler`: avoid validating JSON when encoding `json.Marshaler`
+- `SearchOption.ValidateJSON`: indicates if validate located JSON value when `Get`
+
+## JSON-Path Support (GJSON)
+
+[tidwall/gjson](https://github.com/tidwall/gjson) has provided a comprehensive and popular JSON-Path API, and
+ a lot of older codes heavily relies on it. Therefore, we provides a wrapper library, which combines gjson's API with sonic's SIMD algorithm to boost up the performance. See [cloudwego/gjson](https://github.com/cloudwego/gjson).
+
 ## Community
 
 Sonic is a subproject of [CloudWeGo](https://www.cloudwego.io/). We are committed to building a cloud native ecosystem.

+ 38 - 13
vendor/github.com/bytedance/sonic/README_ZH_CN.md

@@ -6,9 +6,10 @@
 
 ## 依赖
 
-- Go 1.16~1.22
-- Linux / MacOS / Windows(需要 Go1.17 以上)
-- Amd64 架构
+- Go: 1.17~1.24
+  - 注意:Go1.24.0 由于 [issue](https://github.com/golang/go/issues/71672) 不可用,请升级到更高 Go 版本,或添加编译选项 `--ldflags="-checklinkname=0"` 
+- OS: Linux / MacOS / Windows
+- CPU: AMD64 / (ARM64, 需要 Go1.20 以上)
 
 ## 接口
 
@@ -260,7 +261,7 @@ fmt.Printf("%+v", data) // {A:0 B:1}
 
 ### `Ast.Node`
 
-Sonic/ast.Node 是完全独立的 JSON 抽象语法树库。它实现了序列化和反序列化,并提供了获取和修改通用数据的鲁棒的 API。
+Sonic/ast.Node 是完全独立的 JSON 抽象语法树库。它实现了序列化和反序列化,并提供了获取和修改JSON数据的鲁棒的 API。
 
 #### 查找/索引
 
@@ -282,6 +283,22 @@ sub := root.Get("key3").Index(2).Int64() // == 3
 
 **注意**:由于 `Index()` 使用偏移量来定位数据,比使用扫描的 `Get()` 要快的多,建议尽可能的使用 `Index` 。 Sonic 也提供了另一个 API, `IndexOrGet()` ,以偏移量为基础并且也确保键的匹配。
 
+#### 查找选项
+
+`ast.Searcher`提供了一些选项,以满足用户的不同需求:
+
+```go
+opts := ast.SearchOption{CopyReturn: true…}
+val, err := sonic.GetWithOptions(JSON, opts, "key")
+```
+
+- CopyReturn
+指示搜索器复制结果JSON字符串,而不是从输入引用。如果用户缓存结果,这有助于减少内存使用
+- ConcurentRead
+因为`ast.Node`使用`Lazy-Load`设计,默认不支持并发读取。如果您想同时读取,请指定它。
+- ValidateJSON
+指示搜索器来验证整个JSON。默认情况下启用该选项, 但是对于查找速度有一定影响。
+
 #### 修改
 
 使用 `Set()` / `Unset()` 修改 json 的内容
@@ -368,16 +385,12 @@ type Visitor interface {
 
 ## 兼容性
 
-由于开发高性能代码的困难性, Sonic **不**保证对所有环境的支持。对于在不同环境中使用 Sonic 构建应用程序的开发者,我们有以下建议:
-
-- 在 **Mac M1** 上开发:确保在您的计算机上安装了 Rosetta 2,并在构建时设置 `GOARCH=amd64` 。 Rosetta 2 可以自动将 x86 二进制文件转换为 arm64 二进制文件,并在 Mac M1 上运行 x86 应用程序。
-- 在 **Linux arm64** 上开发:您可以安装 qemu 并使用 `qemu-x86_64 -cpu max` 命令来将 x86 二进制文件转换为 arm64 二进制文件。qemu可以实现与Mac M1上的Rosetta 2类似的转换效果。
+对于想要使用sonic来满足不同场景的开发人员,我们提供了一些集成配置:
 
-对于希望在不使用 qemu 下使用 sonic 的开发者,或者希望处理 JSON 时与 `encoding/JSON` 严格保持一致的开发者,我们在 `sonic.API` 中提供了一些兼容性 API
-
-- `ConfigDefault`: 在支持 sonic 的环境下 sonic 的默认配置(`EscapeHTML=false`,`SortKeys=false`等)。行为与具有相应配置的 `encoding/json` 一致,一些选项,如 `SortKeys=false` 将无效。
-- `ConfigStd`: 在支持 sonic 的环境下与标准库兼容的配置(`EscapeHTML=true`,`SortKeys=true`等)。行为与 `encoding/json` 一致。
-- `ConfigFastest`: 在支持 sonic 的环境下运行最快的配置(`NoQuoteTextMarshaler=true`)。行为与具有相应配置的 `encoding/json` 一致,某些选项将无效。
+- `ConfigDefault`: sonic的默认配置 (`EscapeHTML=false`, `SortKeys=false`…) 保证性能同时兼顾安全性。
+- `ConfigStd`: 与 `encoding/json` 保证完全兼容的配置
+- `ConfigFastest`: 最快的配置(`NoQuoteTextMarshaler=true...`) 保证性能最优但是会缺少一些安全性检查(validate UTF8 等)
+Sonic **不**确保支持所有环境,由于开发高性能代码的困难。在不支持sonic的环境中,实现将回落到 `encoding/json`。因此上述配置将全部等于`ConfigStd`。
 
 ## 注意事项
 
@@ -464,6 +477,18 @@ go someFunc(user)
 
 但是,`ast.Visitor` 并不是一个很易用的 API。你可能需要写大量的代码去实现自己的 `ast.Visitor`,并且需要在解析过程中仔细维护树的层级。如果你决定要使用这个 API,请先仔细阅读 [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) 中的注释。
 
+### 缓冲区大小
+
+Sonic在许多地方使用内存池,如`encoder.Encode`, `ast.Node.MarshalJSON`等来提高性能,这可能会在服务器负载高时产生更多的内存使用(in-use)。参见[issue 614](https://github.com/bytedance/sonic/issues/614)。因此,我们引入了一些选项来让用户配置内存池的行为。参见[option](https://pkg.go.dev/github.com/bytedance/sonic@v1.11.9/option#pkg-variables)包。
+
+### 更快的 JSON Skip
+
+为了安全起见,在跳过原始JSON 时,sonic decoder 默认使用[FSM](native/skip_one.c)算法扫描来跳过同时校验 JSON。它相比[SIMD-searching-pair](native/skip_one_fast.c)算法跳过要慢得多(1~10倍)。如果用户有很多冗余的JSON值,并且不需要严格验证JSON的正确性,你可以启用以下选项:
+
+- `Config.NoValidateSkipJSON`: 用于在解码时更快地跳过JSON,例如未知字段,`json.RawMessage`,不匹配的值和冗余的数组元素等
+- `Config.NoValidateJSONMarshaler`: 编码JSON时避免验证JSON。封送拆收器
+- `SearchOption.ValidateJSON`: 指示当`Get`时是否验证定位的JSON值
+
 ## 社区
 
 Sonic 是 [CloudWeGo](https://www.cloudwego.io/) 下的一个子项目。我们致力于构建云原生生态系统。

+ 35 - 2
vendor/github.com/bytedance/sonic/api.go

@@ -23,6 +23,16 @@ import (
     `github.com/bytedance/sonic/internal/rt`
 )
 
+const (
+    // UseStdJSON indicates you are using fallback implementation (encoding/json)
+	UseStdJSON = iota
+    // UseSonicJSON indicates you are using real sonic implementation
+	UseSonicJSON
+)
+
+// APIKind is the kind of API, 0 is std json, 1 is sonic.
+const APIKind = apiKind
+
 // Config is a combination of sonic/encoder.Options and sonic/decoder.Options
 type Config struct {
     // EscapeHTML indicates encoder to escape all HTML characters 
@@ -67,16 +77,23 @@ type Config struct {
     // CopyString indicates decoder to decode string values by copying instead of referring.
     CopyString                    bool
 
-    // ValidateString indicates decoder and encoder to valid string values: decoder will return errors 
+    // ValidateString indicates decoder and encoder to validate string values: decoder will return errors 
     // when unescaped control chars(\u0000-\u001f) in the string value of JSON.
     ValidateString                bool
 
     // NoValidateJSONMarshaler indicates that the encoder should not validate the output string
     // after encoding the JSONMarshaler to JSON.
     NoValidateJSONMarshaler       bool
+
+    // NoValidateJSONSkip indicates the decoder should not validate the JSON value when skipping it,
+    // such as unknown-fields, mismatched-type, redundant elements..
+    NoValidateJSONSkip bool
     
     // NoEncoderNewline indicates that the encoder should not add a newline after every message
     NoEncoderNewline bool
+
+    // Encode Infinity or Nan float into `null`, instead of returning an error.
+    EncodeNullForInfOrNan bool
 }
  
 var (
@@ -96,13 +113,14 @@ var (
     ConfigFastest = Config{
         NoQuoteTextMarshaler: true,
         NoValidateJSONMarshaler: true,
+        NoValidateJSONSkip: true,
     }.Froze()
 )
  
  
 // API is a binding of specific config.
 // This interface is inspired by github.com/json-iterator/go,
-// and has same behaviors under equavilent config.
+// and has same behaviors under equivalent config.
 type API interface {
     // MarshalToString returns the JSON encoding string of v
     MarshalToString(v interface{}) (string, error)
@@ -157,6 +175,13 @@ func Marshal(val interface{}) ([]byte, error) {
     return ConfigDefault.Marshal(val)
 }
 
+// MarshalIndent is like Marshal but applies Indent to format the output.
+// Each JSON element in the output will begin on a new line beginning with prefix
+// followed by one or more copies of indent according to the indentation nesting.
+func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
+    return ConfigDefault.MarshalIndent(v, prefix, indent)
+}
+
 // MarshalString returns the JSON encoding string of v.
 func MarshalString(val interface{}) (string, error) {
     return ConfigDefault.MarshalToString(val)
@@ -189,6 +214,14 @@ func Get(src []byte, path ...interface{}) (ast.Node, error) {
     return GetCopyFromString(rt.Mem2Str(src), path...)
 }
 
+//GetWithOptions searches and locates the given path from src json,
+// with specific options of ast.Searcher
+func GetWithOptions(src []byte, opts ast.SearchOptions, path ...interface{}) (ast.Node, error) {
+    s := ast.NewSearcher(rt.Mem2Str(src))
+    s.SearchOptions = opts
+    return s.GetByPath(path...)
+}
+
 // GetFromString is same with Get except src is string.
 //
 // WARNING: The returned JSON is **Referenced** from the input. 

+ 3 - 3
vendor/github.com/bytedance/sonic/ast/api.go

@@ -1,5 +1,5 @@
-//go:build (amd64 && go1.16 && !go1.23) || (arm64 && go1.20 && !go1.23)
-// +build amd64,go1.16,!go1.23 arm64,go1.20,!go1.23
+//go:build (amd64 && go1.17 && !go1.25) || (arm64 && go1.20 && !go1.25)
+// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25
 
 /*
  * Copyright 2022 ByteDance Inc.
@@ -61,7 +61,7 @@ func quote(buf *[]byte, val string) {
         }
 
         // double buf size
-        *b = growslice(typeByte, *b, b.Cap*2)
+        *b = rt.GrowSlice(typeByte, *b, b.Cap*2)
         // ret is the complement of consumed input
         ret = ^ret
         // update input buffer

+ 4 - 3
vendor/github.com/bytedance/sonic/ast/api_compat.go

@@ -1,4 +1,4 @@
-// +build !amd64,!arm64 go1.23 !go1.16 arm64,!go1.20
+// +build !amd64,!arm64 go1.25 !go1.17 arm64,!go1.20
 
 /*
 * Copyright 2022 ByteDance Inc.
@@ -24,17 +24,18 @@ import (
 
     `github.com/bytedance/sonic/internal/native/types`
     `github.com/bytedance/sonic/internal/rt`
+    `github.com/bytedance/sonic/internal/compat`
 )
 
 func init() {
-    println("WARNING:(ast) sonic only supports Go1.16~1.22, but your environment is not suitable")
+    compat.Warn("sonic/ast")
 }
 
 func quote(buf *[]byte, val string) {
     quoteString(buf, val)
 }
 
-// unquote unescapes a internal JSON string (it doesn't count quotas at the begining and end)
+// unquote unescapes an internal JSON string (it doesn't count quotas at the beginning and end)
 func unquote(src string) (string, types.ParsingError) {
     sp := rt.IndexChar(src, -1)
     out, ok := unquoteBytes(rt.BytesFrom(sp, len(src)+2, len(src)+2))

+ 75 - 14
vendor/github.com/bytedance/sonic/ast/buffer.go

@@ -17,8 +17,10 @@
 package ast
 
 import (
-    `sort`
-    `unsafe`
+	"sort"
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/caching"
 )
 
 type nodeChunk [_DEFAULT_NODE_CAP]Node
@@ -90,18 +92,11 @@ func (self *linkedNodes) Pop() {
     self.size--
 }
 
-func (self *linkedPairs) Pop() {
-    if self == nil || self.size == 0 {
-        return
-    }
-    self.Set(self.size-1, Pair{})
-    self.size--
-}
-
 func (self *linkedNodes) Push(v Node) {
     self.Set(self.size, v)
 }
 
+
 func (self *linkedNodes) Set(i int, v Node) {
     if i < _DEFAULT_NODE_CAP {
         self.head[i] = v
@@ -195,11 +190,22 @@ func (self *linkedNodes) FromSlice(con []Node) {
 type pairChunk [_DEFAULT_NODE_CAP]Pair
 
 type linkedPairs struct {
+    index map[uint64]int
     head pairChunk
     tail []*pairChunk
     size int
 }
 
+func (self *linkedPairs) BuildIndex() {
+    if self.index == nil {
+        self.index = make(map[uint64]int, self.size)
+    }
+    for i:=0; i<self.size; i++ {
+        p := self.At(i)
+        self.index[p.hash] = i
+    }
+}
+
 func (self *linkedPairs) Cap() int {
     if self == nil {
         return 0
@@ -233,7 +239,31 @@ func (self *linkedPairs) Push(v Pair) {
     self.Set(self.size, v)
 }
 
+func (self *linkedPairs) Pop() {
+    if self == nil || self.size == 0 {
+        return
+    }
+    self.Unset(self.size-1)
+    self.size--
+}
+
+func (self *linkedPairs) Unset(i int) {
+    if self.index != nil {
+        p := self.At(i)
+        delete(self.index, p.hash)
+    }
+    self.set(i, Pair{}) 
+}
+
 func (self *linkedPairs) Set(i int, v Pair) {
+    if self.index != nil {
+        h := v.hash
+        self.index[h] = i
+    }
+    self.set(i, v)
+}
+
+func (self *linkedPairs) set(i int, v Pair) {
     if i < _DEFAULT_NODE_CAP {
         self.head[i] = v
         if self.size <= i {
@@ -276,6 +306,21 @@ func (self *linkedPairs) growTailLength(l int) {
 
 // linear search
 func (self *linkedPairs) Get(key string) (*Pair, int) {
+    if self.index != nil {
+        // fast-path
+        i, ok := self.index[caching.StrHash(key)]
+        if ok {
+            n := self.At(i)
+            if n.Key == key {
+                return n, i
+            }
+            // hash conflicts
+            goto linear_search
+        } else {
+            return nil, -1
+        }
+    }
+linear_search:
     for i:=0; i<self.size; i++ {
         if n := self.At(i); n.Key == key {
             return n, i
@@ -313,15 +358,27 @@ func (self *linkedPairs) ToMap(con map[string]Node) {
     }
 }
 
+func (self *linkedPairs) copyPairs(to []Pair, from []Pair, l int) {
+    copy(to, from)
+    if self.index != nil {
+        for i:=0; i<l; i++ {
+            // NOTICE: in case of user not pass hash, just cal it
+            h := caching.StrHash(from[i].Key)
+            from[i].hash = h
+            self.index[h] = i
+        }
+    }
+}
+
 func (self *linkedPairs) FromSlice(con []Pair) {
     self.size = len(con)
     i := self.size-1
     a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
     if a < 0 {
-        copy(self.head[:b+1], con)
+        self.copyPairs(self.head[:b+1], con, b+1)
         return
     } else {
-        copy(self.head[:], con)
+        self.copyPairs(self.head[:], con, len(self.head))
         con = con[_DEFAULT_NODE_CAP:]
     }
 
@@ -333,12 +390,12 @@ func (self *linkedPairs) FromSlice(con []Pair) {
 
     for i:=0; i<a; i++ {
         self.tail[i] = new(pairChunk)
-        copy(self.tail[i][:], con)
+        self.copyPairs(self.tail[i][:], con, len(self.tail[i]))
         con = con[_DEFAULT_NODE_CAP:]
     }
 
     self.tail[a] = new(pairChunk)
-    copy(self.tail[a][:b+1], con)
+    self.copyPairs(self.tail[a][:b+1], con, b+1)
 }
 
 func (self *linkedPairs) Less(i, j int) bool {
@@ -347,6 +404,10 @@ func (self *linkedPairs) Less(i, j int) bool {
 
 func (self *linkedPairs) Swap(i, j int) {
     a, b := self.At(i), self.At(j)
+    if self.index != nil {
+        self.index[a.hash] = j
+        self.index[b.hash] = i
+    }
     *a, *b = *b, *a
 }
 

+ 16 - 72
vendor/github.com/bytedance/sonic/ast/decode.go

@@ -17,19 +17,23 @@
 package ast
 
 import (
-    `encoding/base64`
-    `runtime`
-    `strconv`
-    `unsafe`
-
-    `github.com/bytedance/sonic/internal/native/types`
-    `github.com/bytedance/sonic/internal/rt`
+	"encoding/base64"
+	"runtime"
+	"strconv"
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/native/types"
+	"github.com/bytedance/sonic/internal/rt"
+	"github.com/bytedance/sonic/internal/utils"
 )
 
-const _blankCharsMask = (1 << ' ') | (1 << '\t') | (1 << '\r') | (1 << '\n')
+// Hack: this is used for both checking space and cause friendly compile errors in 32-bit arch.
+const _Sonic_Not_Support_32Bit_Arch__Checking_32Bit_Arch_Here = (1 << ' ') | (1 << '\t') | (1 << '\r') | (1 << '\n')
+
+var bytesNull   = []byte("null")
 
 const (
-    bytesNull   = "null"
+    strNull   = "null"
     bytesTrue   = "true"
     bytesFalse  = "false"
     bytesObject = "{}"
@@ -37,7 +41,7 @@ const (
 )
 
 func isSpace(c byte) bool {
-    return (int(1<<c) & _blankCharsMask) != 0
+    return (int(1<<c) & _Sonic_Not_Support_32Bit_Arch__Checking_32Bit_Arch_Here) != 0
 }
 
 //go:nocheckptr
@@ -63,7 +67,7 @@ func decodeNull(src string, pos int) (ret int) {
     if ret > len(src) {
         return -int(types.ERR_EOF)
     }
-    if src[pos:ret] == bytesNull {
+    if src[pos:ret] == strNull {
         return ret
     } else {
         return -int(types.ERR_INVALID_CHAR)
@@ -287,67 +291,7 @@ func decodeValue(src string, pos int, skipnum bool) (ret int, v types.JsonState)
 
 //go:nocheckptr
 func skipNumber(src string, pos int) (ret int) {
-    sp := uintptr(rt.IndexChar(src, pos))
-    se := uintptr(rt.IndexChar(src, len(src)))
-    if uintptr(sp) >= se {
-        return -int(types.ERR_EOF)
-    }
-
-    if c := *(*byte)(unsafe.Pointer(sp)); c == '-' {
-        sp += 1
-    }
-    ss := sp
-
-    var pointer bool
-    var exponent bool
-    var lastIsDigit bool
-    var nextNeedDigit = true
-
-    for ; sp < se; sp += uintptr(1) {
-        c := *(*byte)(unsafe.Pointer(sp))
-        if isDigit(c) {
-            lastIsDigit = true
-            nextNeedDigit = false
-            continue
-        } else if nextNeedDigit {
-            return -int(types.ERR_INVALID_CHAR)
-        } else if c == '.' {
-            if !lastIsDigit || pointer || exponent || sp == ss {
-                return -int(types.ERR_INVALID_CHAR)
-            }
-            pointer = true
-            lastIsDigit = false
-            nextNeedDigit = true
-            continue
-        } else if c == 'e' || c == 'E' {
-            if !lastIsDigit || exponent {
-                return -int(types.ERR_INVALID_CHAR)
-            }
-            if sp == se-1 {
-                return -int(types.ERR_EOF)
-            }
-            exponent = true
-            lastIsDigit = false
-            nextNeedDigit = false
-            continue
-        } else if c == '-' || c == '+' {
-            if prev := *(*byte)(unsafe.Pointer(sp - 1)); prev != 'e' && prev != 'E' {
-                return -int(types.ERR_INVALID_CHAR)
-            }
-            lastIsDigit = false
-            nextNeedDigit = true
-            continue
-        } else {
-            break
-        }
-    }
-
-    if nextNeedDigit {
-        return -int(types.ERR_EOF)
-    }
-
-    runtime.KeepAlive(src)
-    return int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr))
+    return utils.SkipNumber(src, pos)
 }
 
 //go:nocheckptr

+ 35 - 20
vendor/github.com/bytedance/sonic/ast/encode.go

@@ -17,12 +17,11 @@
 package ast
 
 import (
-    `sync`
-    `unicode/utf8`
-)
+	"sync"
+	"unicode/utf8"
 
-const (
-    _MaxBuffer = 1024    // 1KB buffer size
+	"github.com/bytedance/sonic/internal/rt"
+    "github.com/bytedance/sonic/option"
 )
 
 func quoteString(e *[]byte, s string) {
@@ -30,7 +29,7 @@ func quoteString(e *[]byte, s string) {
     start := 0
     for i := 0; i < len(s); {
         if b := s[i]; b < utf8.RuneSelf {
-            if safeSet[b] {
+            if rt.SafeSet[b] {
                 i++
                 continue
             }
@@ -54,8 +53,8 @@ func quoteString(e *[]byte, s string) {
                 // user-controlled strings are rendered into JSON
                 // and served to some browsers.
                 *e = append(*e, `u00`...)
-                *e = append(*e, hex[b>>4])
-                *e = append(*e, hex[b&0xF])
+                *e = append(*e, rt.Hex[b>>4])
+                *e = append(*e, rt.Hex[b&0xF])
             }
             i++
             start = i
@@ -76,7 +75,7 @@ func quoteString(e *[]byte, s string) {
                 *e = append(*e, s[start:i]...)
             }
             *e = append(*e, `\u202`...)
-            *e = append(*e, hex[c&0xF])
+            *e = append(*e, rt.Hex[c&0xF])
             i += size
             start = i
             continue
@@ -92,16 +91,24 @@ func quoteString(e *[]byte, s string) {
 var bytesPool   = sync.Pool{}
 
 func (self *Node) MarshalJSON() ([]byte, error) {
+	if self == nil {
+		return bytesNull, nil
+	}
+
     buf := newBuffer()
     err := self.encode(buf)
     if err != nil {
         freeBuffer(buf)
         return nil, err
     }
-
-    ret := make([]byte, len(*buf))
-    copy(ret, *buf)
-    freeBuffer(buf)
+    var ret []byte
+    if !rt.CanSizeResue(cap(*buf)) {
+        ret = *buf
+    } else {
+        ret = make([]byte, len(*buf))
+        copy(ret, *buf)
+        freeBuffer(buf)
+    }
     return ret, err
 }
 
@@ -109,21 +116,24 @@ func newBuffer() *[]byte {
     if ret := bytesPool.Get(); ret != nil {
         return ret.(*[]byte)
     } else {
-        buf := make([]byte, 0, _MaxBuffer)
+        buf := make([]byte, 0, option.DefaultAstBufferSize)
         return &buf
     }
 }
 
 func freeBuffer(buf *[]byte) {
+    if !rt.CanSizeResue(cap(*buf)) {
+        return
+    }
     *buf = (*buf)[:0]
     bytesPool.Put(buf)
 }
 
 func (self *Node) encode(buf *[]byte) error {
-    if self.IsRaw() {
+    if self.isRaw() {
         return self.encodeRaw(buf)
     }
-    switch self.Type() {
+    switch int(self.itype()) {
         case V_NONE  : return ErrNotExist
         case V_ERROR : return self.Check()
         case V_NULL  : return self.encodeNull(buf)
@@ -139,16 +149,21 @@ func (self *Node) encode(buf *[]byte) error {
 }
 
 func (self *Node) encodeRaw(buf *[]byte) error {
-    raw, err := self.Raw()
-    if err != nil {
-        return err
+    lock := self.rlock()
+    if !self.isRaw() {
+        self.runlock()
+        return self.encode(buf)
+    }
+    raw := self.toString()
+    if lock {
+        self.runlock()
     }
     *buf = append(*buf, raw...)
     return nil
 }
 
 func (self *Node) encodeNull(buf *[]byte) error {
-    *buf = append(*buf, bytesNull...)
+    *buf = append(*buf, strNull...)
     return nil
 }
 

+ 5 - 1
vendor/github.com/bytedance/sonic/ast/error.go

@@ -17,6 +17,10 @@ func newError(err types.ParsingError, msg string) *Node {
     }
 }
 
+func newErrorPair(err SyntaxError) *Pair {
+   return &Pair{0, "", *newSyntaxError(err)}
+}
+
 // Error returns error message if the node is invalid
 func (self Node) Error() string {
     if self.t != V_ERROR {
@@ -79,7 +83,7 @@ func (self SyntaxError) description() string {
 
     /* check for empty source */
     if self.Src == "" {
-        return fmt.Sprintf("no sources available: %#v", self)
+        return fmt.Sprintf("no sources available, the input json is empty: %#v", self)
     }
 
     /* prevent slicing before the beginning */

+ 18 - 5
vendor/github.com/bytedance/sonic/ast/iterator.go

@@ -17,19 +17,29 @@
 package ast
 
 import (
-    `fmt`
+	"fmt"
 
-    `github.com/bytedance/sonic/internal/native/types`
+	"github.com/bytedance/sonic/internal/caching"
+	"github.com/bytedance/sonic/internal/native/types"
 )
 
 type Pair struct {
+    hash  uint64
     Key   string
     Value Node
 }
 
+func NewPair(key string, val Node) Pair {
+    return Pair{
+        hash: caching.StrHash(key),
+        Key: key,
+        Value: val,
+    }
+}
+
 // Values returns iterator for array's children traversal
 func (self *Node) Values() (ListIterator, error) {
-    if err := self.should(types.V_ARRAY, "an array"); err != nil {
+    if err := self.should(types.V_ARRAY); err != nil {
         return ListIterator{}, err
     }
     return self.values(), nil
@@ -41,7 +51,7 @@ func (self *Node) values() ListIterator {
 
 // Properties returns iterator for object's children traversal
 func (self *Node) Properties() (ObjectIterator, error) {
-    if err := self.should(types.V_OBJECT, "an object"); err != nil {
+    if err := self.should(types.V_OBJECT); err != nil {
         return ObjectIterator{}, err
     }
     return self.properties(), nil
@@ -163,11 +173,14 @@ type Scanner func(path Sequence, node *Node) bool
 // ForEach scans one V_OBJECT node's children from JSON head to tail, 
 // and pass the Sequence and Node of corresponding JSON value.
 //
-// Especailly, if the node is not V_ARRAY or V_OBJECT, 
+// Especially, if the node is not V_ARRAY or V_OBJECT,
 // the node itself will be returned and Sequence.Index == -1.
 // 
 // NOTICE: A unsetted node WON'T trigger sc, but its index still counts into Path.Index
 func (self *Node) ForEach(sc Scanner) error {
+    if err := self.checkRaw(); err != nil {
+        return err
+    }
     switch self.itype() {
     case types.V_ARRAY:
         iter, err := self.Values()

+ 172 - 153
vendor/github.com/bytedance/sonic/ast/node.go

@@ -17,13 +17,15 @@
 package ast
 
 import (
-    `encoding/json`
-    `fmt`
-    `strconv`
-    `unsafe`
-    
-    `github.com/bytedance/sonic/internal/native/types`
-    `github.com/bytedance/sonic/internal/rt`
+	"encoding/json"
+	"fmt"
+	"strconv"
+	"sync"
+	"sync/atomic"
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/native/types"
+	"github.com/bytedance/sonic/internal/rt"
 )
 
 const (
@@ -36,7 +38,7 @@ const (
     _V_ARRAY_LAZY                   = _V_LAZY | types.V_ARRAY
     _V_OBJECT_LAZY                  = _V_LAZY | types.V_OBJECT
     _MASK_LAZY                      = _V_LAZY - 1
-    _MASK_RAW                       = _V_RAW - 1
+    _MASK_RAW                      = _V_RAW - 1
 )
 
 const (
@@ -56,6 +58,7 @@ type Node struct {
     t types.ValueType
     l uint
     p unsafe.Pointer
+    m *sync.RWMutex
 }
 
 // UnmarshalJSON is just an adapter to json.Unmarshaler.
@@ -68,7 +71,7 @@ func (self *Node) UnmarshalJSON(data []byte) (err error) {
 /** Node Type Accessor **/
 
 // Type returns json type represented by the node
-// It will be one of belows:
+// It will be one of bellows:
 //    V_NONE   = 0 (empty node, key not exists)
 //    V_ERROR  = 1 (error node)
 //    V_NULL   = 2 (json value `null`, key exists)
@@ -79,17 +82,39 @@ func (self *Node) UnmarshalJSON(data []byte) (err error) {
 //    V_STRING = 7 (json value string)
 //    V_NUMBER = 33 (json value number )
 //    V_ANY    = 34 (golang interface{})
+//
+// Deprecated: not concurrent safe. Use TypeSafe instead
 func (self Node) Type() int {
     return int(self.t & _MASK_LAZY & _MASK_RAW)
 }
 
-func (self Node) itype() types.ValueType {
+// Type concurrently-safe returns json type represented by the node
+// It will be one of bellows:
+//    V_NONE   = 0 (empty node, key not exists)
+//    V_ERROR  = 1 (error node)
+//    V_NULL   = 2 (json value `null`, key exists)
+//    V_TRUE   = 3 (json value `true`)
+//    V_FALSE  = 4 (json value `false`)
+//    V_ARRAY  = 5 (json value array)
+//    V_OBJECT = 6 (json value object)
+//    V_STRING = 7 (json value string)
+//    V_NUMBER = 33 (json value number )
+//    V_ANY    = 34 (golang interface{})
+func (self *Node) TypeSafe() int {
+    return int(self.loadt() & _MASK_LAZY & _MASK_RAW)
+}
+
+func (self *Node) itype() types.ValueType {
     return self.t & _MASK_LAZY & _MASK_RAW
 }
 
 // Exists returns false only if the self is nil or empty node V_NONE
 func (self *Node) Exists() bool {
-    return self.Valid() && self.t != _V_NONE
+    if self == nil {
+        return false
+    }
+    t := self.loadt()
+    return t != V_ERROR && t != _V_NONE
 }
 
 // Valid reports if self is NOT V_ERROR or nil
@@ -97,7 +122,7 @@ func (self *Node) Valid() bool {
     if self == nil {
         return false
     }
-    return self.t != V_ERROR
+    return self.loadt() != V_ERROR
 }
 
 // Check checks if the node itself is valid, and return:
@@ -106,24 +131,31 @@ func (self *Node) Valid() bool {
 func (self *Node)  Check() error {
     if self == nil {
         return ErrNotExist
-    } else if self.t != V_ERROR {
+    } else if self.loadt() != V_ERROR {
         return nil
     } else {
         return self
     }
 }
 
-// IsRaw returns true if node's underlying value is raw json
+// isRaw returns true if node's underlying value is raw json
+//
+// Deprecated: not concurrent safe
 func (self Node) IsRaw() bool {
-    return self.t&_V_RAW != 0
+    return self.t & _V_RAW != 0
+}
+
+// IsRaw returns true if node's underlying value is raw json
+func (self *Node) isRaw() bool {
+    return self.loadt() & _V_RAW != 0
 }
 
 func (self *Node) isLazy() bool {
-    return self != nil && self.t&_V_LAZY != 0
+    return self != nil && self.t & _V_LAZY != 0
 }
 
 func (self *Node) isAny() bool {
-    return self != nil && self.t == _V_ANY
+    return self != nil && self.loadt() == _V_ANY
 }
 
 /** Simple Value Methods **/
@@ -133,18 +165,26 @@ func (self *Node) Raw() (string, error) {
     if self == nil {
         return "", ErrNotExist
     }
-    if !self.IsRaw() {
+    lock := self.rlock()
+    if !self.isRaw() {
+        if lock {
+            self.runlock()
+        }
         buf, err := self.MarshalJSON()
         return rt.Mem2Str(buf), err
     }
-    return self.toString(), nil
+    ret := self.toString()
+    if lock {
+        self.runlock()
+    }
+    return ret, nil
 }
 
 func (self *Node) checkRaw() error {
     if err := self.Check(); err != nil {
         return err
     }
-    if self.IsRaw() {
+    if self.isRaw() {
         self.parseRaw(false)
     }
     return self.Check()
@@ -400,7 +440,7 @@ func (self *Node) String() (string, error) {
     }
 }
 
-// StrictString returns string value (unescaped), includeing V_STRING, V_ANY of string.
+// StrictString returns string value (unescaped), including V_STRING, V_ANY of string.
 // In other cases, it will return empty string.
 func (self *Node) StrictString() (string, error) {
     if err := self.checkRaw(); err != nil {
@@ -469,7 +509,7 @@ func (self *Node) Float64() (float64, error) {
     }
 }
 
-// Float64 exports underlying float64 value, includeing V_NUMBER, V_ANY 
+// Float64 exports underlying float64 value, including V_NUMBER, V_ANY
 func (self *Node) StrictFloat64() (float64, error) {
     if err := self.checkRaw(); err != nil {
         return 0.0, err
@@ -487,7 +527,7 @@ func (self *Node) StrictFloat64() (float64, error) {
     }
 }
 
-/** Sequencial Value Methods **/
+/** Sequential Value Methods **/
 
 // Len returns children count of a array|object|string node
 // WARN: For partially loaded node, it also works but only counts the parsed children
@@ -504,7 +544,7 @@ func (self *Node) Len() (int, error) {
     }
 }
 
-func (self Node) len() int {
+func (self *Node) len() int {
     return int(self.l)
 }
 
@@ -527,7 +567,7 @@ func (self *Node) Cap() (int, error) {
 //
 // If self is V_NONE or V_NULL, it becomes V_OBJECT and sets the node at the key.
 func (self *Node) Set(key string, node Node) (bool, error) {
-    if err := self.Check(); err != nil {
+    if err := self.checkRaw(); err != nil {
         return false, err
     }
     if err := node.Check(); err != nil {
@@ -535,7 +575,7 @@ func (self *Node) Set(key string, node Node) (bool, error) {
     }
     
     if self.t == _V_NONE || self.t == types.V_NULL {
-        *self = NewObject([]Pair{{key, node}})
+        *self = NewObject([]Pair{NewPair(key, node)})
         return false, nil
     } else if self.itype() != types.V_OBJECT {
         return false, ErrUnsupportType
@@ -549,7 +589,7 @@ func (self *Node) Set(key string, node Node) (bool, error) {
             *self = newObject(new(linkedPairs))
         }
         s := (*linkedPairs)(self.p)
-        s.Push(Pair{key, node})
+        s.Push(NewPair(key, node))
         self.l++
         return false, nil
 
@@ -568,10 +608,10 @@ func (self *Node) SetAny(key string, val interface{}) (bool, error) {
 
 // Unset REMOVE (soft) the node of given key under object parent, and reports if the key has existed.
 func (self *Node) Unset(key string) (bool, error) {
-    if err := self.should(types.V_OBJECT, "an object"); err != nil {
+    if err := self.should(types.V_OBJECT); err != nil {
         return false, err
     }
-    // NOTICE: must get acurate length before deduct
+    // NOTICE: must get accurate length before deduct
     if err := self.skipAllKey(); err != nil {
         return false, err
     }
@@ -589,7 +629,7 @@ func (self *Node) Unset(key string) (bool, error) {
 //
 // The index must be within self's children.
 func (self *Node) SetByIndex(index int, node Node) (bool, error) {
-    if err := self.Check(); err != nil {
+    if err := self.checkRaw(); err != nil {
         return false, err 
     }
     if err := node.Check(); err != nil {
@@ -617,7 +657,7 @@ func (self *Node) SetAnyByIndex(index int, val interface{}) (bool, error) {
     return self.SetByIndex(index, NewAny(val))
 }
 
-// UnsetByIndex REOMVE (softly) the node of given index.
+// UnsetByIndex REMOVE (softly) the node of given index.
 //
 // WARN: this will change address of elements, which is a dangerous action.
 // Use Unset() for object or Pop() for array instead.
@@ -669,7 +709,7 @@ func (self *Node) UnsetByIndex(index int) (bool, error) {
 //
 // If self is V_NONE or V_NULL, it becomes V_ARRAY and sets the node at index 0.
 func (self *Node) Add(node Node) error {
-    if err := self.Check(); err != nil {
+    if err := self.checkRaw(); err != nil {
         return err
     }
 
@@ -677,7 +717,7 @@ func (self *Node) Add(node Node) error {
         *self = NewArray([]Node{node})
         return nil
     }
-    if err := self.should(types.V_ARRAY, "an array"); err != nil {
+    if err := self.should(types.V_ARRAY); err != nil {
         return err
     }
 
@@ -740,7 +780,7 @@ func (self *Node) Pop() error {
 // 
 // WARN: this will change address of elements, which is a dangerous action.
 func (self *Node) Move(dst, src int) error {
-    if err := self.should(types.V_ARRAY, "an array"); err != nil {
+    if err := self.should(types.V_ARRAY); err != nil {
         return err
     }
 
@@ -812,7 +852,7 @@ func (self *Node) GetByPath(path ...interface{}) *Node {
 
 // Get loads given key of an object node on demands
 func (self *Node) Get(key string) *Node {
-    if err := self.should(types.V_OBJECT, "an object"); err != nil {
+    if err := self.should(types.V_OBJECT); err != nil {
         return unwrapError(err)
     }
     n, _ := self.skipKey(key)
@@ -845,14 +885,14 @@ func (self *Node) Index(idx int) *Node {
 // IndexPair indexies pair at given idx,
 // node type MUST be either V_OBJECT
 func (self *Node) IndexPair(idx int) *Pair {
-    if err := self.should(types.V_OBJECT, "an object"); err != nil {
+    if err := self.should(types.V_OBJECT); err != nil {
         return nil
     }
     return self.skipIndexPair(idx)
 }
 
 func (self *Node) indexOrGet(idx int, key string) (*Node, int) {
-    if err := self.should(types.V_OBJECT, "an object"); err != nil {
+    if err := self.should(types.V_OBJECT); err != nil {
         return unwrapError(err), idx
     }
 
@@ -889,10 +929,10 @@ func (self *Node) Map() (map[string]interface{}, error) {
             return nil, ErrUnsupportType
         }
     }
-    if err := self.should(types.V_OBJECT, "an object"); err != nil {
+    if err := self.should(types.V_OBJECT); err != nil {
         return nil, err
     }
-    if err := self.loadAllKey(); err != nil {
+    if err := self.loadAllKey(false); err != nil {
         return nil, err
     }
     return self.toGenericObject()
@@ -908,16 +948,16 @@ func (self *Node) MapUseNumber() (map[string]interface{}, error) {
             return nil, ErrUnsupportType
         }
     }
-    if err := self.should(types.V_OBJECT, "an object"); err != nil {
+    if err := self.should(types.V_OBJECT); err != nil {
         return nil, err
     }
-    if err := self.loadAllKey(); err != nil {
+    if err := self.loadAllKey(false); err != nil {
         return nil, err
     }
     return self.toGenericObjectUseNumber()
 }
 
-// MapUseNode scans both parsed and non-parsed chidren nodes, 
+// MapUseNode scans both parsed and non-parsed children nodes,
 // and map them by their keys
 func (self *Node) MapUseNode() (map[string]Node, error) {
     if self.isAny() {
@@ -928,7 +968,7 @@ func (self *Node) MapUseNode() (map[string]Node, error) {
             return nil, ErrUnsupportType
         }
     }
-    if err := self.should(types.V_OBJECT, "an object"); err != nil {
+    if err := self.should(types.V_OBJECT); err != nil {
         return nil, err
     }
     if err := self.skipAllKey(); err != nil {
@@ -1034,10 +1074,10 @@ func (self *Node) Array() ([]interface{}, error) {
             return nil, ErrUnsupportType
         }
     }
-    if err := self.should(types.V_ARRAY, "an array"); err != nil {
+    if err := self.should(types.V_ARRAY); err != nil {
         return nil, err
     }
-    if err := self.loadAllIndex(); err != nil {
+    if err := self.loadAllIndex(false); err != nil {
         return nil, err
     }
     return self.toGenericArray()
@@ -1053,16 +1093,16 @@ func (self *Node) ArrayUseNumber() ([]interface{}, error) {
             return nil, ErrUnsupportType
         }
     }
-    if err := self.should(types.V_ARRAY, "an array"); err != nil {
+    if err := self.should(types.V_ARRAY); err != nil {
         return nil, err
     }
-    if err := self.loadAllIndex(); err != nil {
+    if err := self.loadAllIndex(false); err != nil {
         return nil, err
     }
     return self.toGenericArrayUseNumber()
 }
 
-// ArrayUseNode copys both parsed and non-parsed chidren nodes, 
+// ArrayUseNode copies both parsed and non-parsed children nodes,
 // and indexes them by original order
 func (self *Node) ArrayUseNode() ([]Node, error) {
     if self.isAny() {
@@ -1073,7 +1113,7 @@ func (self *Node) ArrayUseNode() ([]Node, error) {
             return nil, ErrUnsupportType
         }
     }
-    if err := self.should(types.V_ARRAY, "an array"); err != nil {
+    if err := self.should(types.V_ARRAY); err != nil {
         return nil, err
     }
     if err := self.skipAllIndex(); err != nil {
@@ -1107,9 +1147,9 @@ func (self *Node) unsafeArray() (*linkedNodes, error) {
     return (*linkedNodes)(self.p), nil
 }
 
-// Interface loads all children under all pathes from this node,
+// Interface loads all children under all paths from this node,
 // and converts itself as generic type.
-// WARN: all numberic nodes are casted to float64
+// WARN: all numeric nodes are casted to float64
 func (self *Node) Interface() (interface{}, error) {
     if err := self.checkRaw(); err != nil {
         return nil, err
@@ -1129,12 +1169,12 @@ func (self *Node) Interface() (interface{}, error) {
             }
             return v, nil
         case _V_ARRAY_LAZY   :
-            if err := self.loadAllIndex(); err != nil {
+            if err := self.loadAllIndex(false); err != nil {
                 return nil, err
             }
             return self.toGenericArray()
         case _V_OBJECT_LAZY  :
-            if err := self.loadAllKey(); err != nil {
+            if err := self.loadAllKey(false); err != nil {
                 return nil, err
             }
             return self.toGenericObject()
@@ -1153,7 +1193,7 @@ func (self *Node) packAny() interface{} {
 }
 
 // InterfaceUseNumber works same with Interface()
-// except numberic nodes  are casted to json.Number
+// except numeric nodes are casted to json.Number
 func (self *Node) InterfaceUseNumber() (interface{}, error) {
     if err := self.checkRaw(); err != nil {
         return nil, err
@@ -1168,12 +1208,12 @@ func (self *Node) InterfaceUseNumber() (interface{}, error) {
         case types.V_STRING  : return self.toString(), nil
         case _V_NUMBER       : return self.toNumber(), nil
         case _V_ARRAY_LAZY   :
-            if err := self.loadAllIndex(); err != nil {
+            if err := self.loadAllIndex(false); err != nil {
                 return nil, err
             }
             return self.toGenericArrayUseNumber()
         case _V_OBJECT_LAZY  :
-            if err := self.loadAllKey(); err != nil {
+            if err := self.loadAllKey(false); err != nil {
                 return nil, err
             }
             return self.toGenericObjectUseNumber()
@@ -1205,70 +1245,30 @@ func (self *Node) InterfaceUseNode() (interface{}, error) {
     }
 }
 
-// LoadAll loads all the node's children and children's children as parsed.
-// After calling it, the node can be safely used on concurrency
+// LoadAll loads the node's children 
+// and ensure all its children can be READ concurrently (include its children's children)
 func (self *Node) LoadAll() error {
-    if self.IsRaw() {
-        self.parseRaw(true)
-        return self.Check()
-    }
-
-    switch self.itype() {
-    case types.V_ARRAY:
-        e := self.len()
-        if err := self.loadAllIndex(); err != nil {
-            return err
-        }
-        for i := 0; i < e; i++ {
-            n := self.nodeAt(i)
-            if n.IsRaw() {
-                n.parseRaw(true)
-            }
-            if err := n.Check(); err != nil {
-                return err
-            }
-        }
-        return nil
-    case types.V_OBJECT:
-        e := self.len()
-        if err := self.loadAllKey(); err != nil {
-            return err
-        }
-        for i := 0; i < e; i++ {
-            n := self.pairAt(i)
-            if n.Value.IsRaw() {
-                n.Value.parseRaw(true)
-            }
-            if err := n.Value.Check(); err != nil {
-                return err
-            }
-        }
-        return nil
-    default:
-        return self.Check()
-    }
+    return self.Load()
 }
 
 // Load loads the node's children as parsed.
-// After calling it, only the node itself can be used on concurrency (not include its children)
+// and ensure all its children can be READ concurrently (include its children's children)
 func (self *Node) Load() error {
-    if err := self.checkRaw(); err != nil {
-        return err
-    }
-
     switch self.t {
-    case _V_ARRAY_LAZY:
-        return self.skipAllIndex()
-    case _V_OBJECT_LAZY:
-        return self.skipAllKey()
-    default:
-        return self.Check()
+        case _V_ARRAY_LAZY: self.loadAllIndex(true)
+        case _V_OBJECT_LAZY: self.loadAllKey(true)
+        case V_ERROR: return self
+        case V_NONE: return nil
+    }
+    if self.m == nil {
+        self.m = new(sync.RWMutex)
     }
+    return self.checkRaw()
 }
 
 /**---------------------------------- Internal Helper Methods ----------------------------------**/
 
-func (self *Node) should(t types.ValueType, s string) error {
+func (self *Node) should(t types.ValueType) error {
     if err := self.checkRaw(); err != nil {
         return err
     }
@@ -1439,13 +1439,17 @@ func (self *Node) skipIndexPair(index int) *Pair {
     return nil
 }
 
-func (self *Node) loadAllIndex() error {
+func (self *Node) loadAllIndex(loadOnce bool) error {
     if !self.isLazy() {
         return nil
     }
     var err types.ParsingError
     parser, stack := self.getParserAndArrayStack()
-    parser.noLazy = true
+    if !loadOnce {
+        parser.noLazy = true
+    } else {
+        parser.loadOnce = true
+    }
     *self, err = parser.decodeArray(&stack.v)
     if err != 0 {
         return parser.ExportError(err)
@@ -1453,14 +1457,19 @@ func (self *Node) loadAllIndex() error {
     return nil
 }
 
-func (self *Node) loadAllKey() error {
+func (self *Node) loadAllKey(loadOnce bool) error {
     if !self.isLazy() {
         return nil
     }
     var err types.ParsingError
     parser, stack := self.getParserAndObjectStack()
-    parser.noLazy = true
-    *self, err = parser.decodeObject(&stack.v)
+    if !loadOnce {
+        parser.noLazy = true
+        *self, err = parser.decodeObject(&stack.v)
+    } else {
+        parser.loadOnce = true
+        *self, err = parser.decodeObject(&stack.v)
+    }
     if err != 0 {
         return parser.ExportError(err)
     }
@@ -1629,7 +1638,23 @@ func NewRaw(json string) Node {
     if it == _V_NONE {
         return Node{}
     }
-    return newRawNode(parser.s[start:parser.p], it)
+    return newRawNode(parser.s[start:parser.p], it, false)
+}
+
+// NewRawConcurrentRead creates a node of raw json, which can be READ 
+// (GetByPath/Get/Index/GetOrIndex/Int64/Bool/Float64/String/Number/Interface/Array/Map/Raw/MarshalJSON) concurrently.
+// If the input json is invalid, NewRaw returns a error Node.
+func NewRawConcurrentRead(json string) Node {
+    parser := NewParserObj(json)
+    start, err := parser.skip()
+    if err != 0 {
+        return *newError(err, err.Message()) 
+    }
+    it := switchRawType(parser.s[start])
+    if it == _V_NONE {
+        return Node{}
+    }
+    return newRawNode(parser.s[start:parser.p], it, true)
 }
 
 // NewAny creates a node of type V_ANY if any's type isn't Node or *Node, 
@@ -1653,7 +1678,7 @@ func NewBytes(src []byte) Node {
     if len(src) == 0 {
         panic("empty src bytes")
     }
-    out := encodeBase64(src)
+    out := rt.EncodeBase64ToString(src)
     return NewString(out)
 }
 
@@ -1689,15 +1714,15 @@ func NewNumber(v string) Node {
     }
 }
 
-func (node Node) toNumber() json.Number {
+func (node *Node) toNumber() json.Number {
     return json.Number(rt.StrFrom(node.p, int64(node.l)))
 }
 
-func (self Node) toString() string {
+func (self *Node) toString() string {
     return rt.StrFrom(self.p, int64(self.l))
 }
 
-func (node Node) toFloat64() (float64, error) {
+func (node *Node) toFloat64() (float64, error) {
     ret, err := node.toNumber().Float64()
     if err != nil {
         return 0, err
@@ -1705,7 +1730,7 @@ func (node Node) toFloat64() (float64, error) {
     return ret, nil
 }
 
-func (node Node) toInt64() (int64, error) {
+func (node *Node) toInt64() (int64, error) {
     ret,err := node.toNumber().Int64()
     if err != nil {
         return 0, err
@@ -1741,6 +1766,8 @@ func NewArray(v []Node) Node {
     return newArray(s)
 }
 
+const _Threshold_Index = 16
+
 func newArray(v *linkedNodes) Node {
     return Node{
         t: types.V_ARRAY,
@@ -1764,6 +1791,9 @@ func NewObject(v []Pair) Node {
 }
 
 func newObject(v *linkedPairs) Node {
+    if v.size > _Threshold_Index {
+        v.BuildIndex()
+    }
     return Node{
         t: types.V_OBJECT,
         l: uint(v.Len()),
@@ -1772,53 +1802,42 @@ func newObject(v *linkedPairs) Node {
 }
 
 func (self *Node) setObject(v *linkedPairs) {
+    if v.size > _Threshold_Index {
+        v.BuildIndex()
+    }
     self.t = types.V_OBJECT
     self.l = uint(v.Len())
     self.p = unsafe.Pointer(v)
 }
 
-func newRawNode(str string, typ types.ValueType) Node {
-    return Node{
-        t: _V_RAW | typ,
-        p: rt.StrPtr(str),
-        l: uint(len(str)),
-    }
-}
-
 func (self *Node) parseRaw(full bool) {
+    lock := self.lock()
+    defer self.unlock()
+    if !self.isRaw() {
+        return
+    }
     raw := self.toString()
     parser := NewParserObj(raw)
+    var e types.ParsingError
     if full {
         parser.noLazy = true
-        parser.skipValue = false
+        *self, e = parser.Parse()
+    } else if lock {
+        var n Node
+        parser.noLazy = true
+        parser.loadOnce = true
+        n, e = parser.Parse()
+        self.assign(n)
+    } else {
+        *self, e = parser.Parse()
     }
-    var e types.ParsingError
-    *self, e = parser.Parse()
     if e != 0 {
         *self = *newSyntaxError(parser.syntaxError(e))
     }
 }
 
-var typeJumpTable = [256]types.ValueType{
-    '"' : types.V_STRING,
-    '-' : _V_NUMBER,
-    '0' : _V_NUMBER,
-    '1' : _V_NUMBER,
-    '2' : _V_NUMBER,
-    '3' : _V_NUMBER,
-    '4' : _V_NUMBER,
-    '5' : _V_NUMBER,
-    '6' : _V_NUMBER,
-    '7' : _V_NUMBER,
-    '8' : _V_NUMBER,
-    '9' : _V_NUMBER,
-    '[' : types.V_ARRAY,
-    'f' : types.V_FALSE,
-    'n' : types.V_NULL,
-    't' : types.V_TRUE,
-    '{' : types.V_OBJECT,
-}
-
-func switchRawType(c byte) types.ValueType {
-    return typeJumpTable[c]
+func (self *Node) assign(n Node) {
+    self.l = n.l
+    self.p = n.p
+    atomic.StoreInt64(&self.t, n.t)
 }

+ 124 - 18
vendor/github.com/bytedance/sonic/ast/parser.go

@@ -17,14 +17,16 @@
 package ast
 
 import (
-    `fmt`
+	"fmt"
+	"sync"
+	"sync/atomic"
 
-    `github.com/bytedance/sonic/internal/native/types`
-    `github.com/bytedance/sonic/internal/rt`
+	"github.com/bytedance/sonic/internal/native/types"
+	"github.com/bytedance/sonic/internal/rt"
 )
 
 const (
-    _DEFAULT_NODE_CAP int = 8
+    _DEFAULT_NODE_CAP int = 16
     _APPEND_GROW_SHIFT = 1
 )
 
@@ -45,6 +47,7 @@ type Parser struct {
     p           int
     s           string
     noLazy      bool
+    loadOnce  bool
     skipValue   bool
     dbuf        *byte
 }
@@ -115,6 +118,10 @@ func (self *Parser) lspace(sp int) int {
     return sp
 }
 
+func (self *Parser) backward() {
+    for ; self.p >= 0 && isSpace(self.s[self.p]); self.p-=1 {}
+}
+
 func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) {
     sp := self.p
     ns := len(self.s)
@@ -148,7 +155,7 @@ func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) {
             if t == _V_NONE {
                 return Node{}, types.ERR_INVALID_CHAR
             }
-            val = newRawNode(self.s[start:self.p], t)
+            val = newRawNode(self.s[start:self.p], t, false)
         }else{
             /* decode the value */
             if val, err = self.Parse(); err != 0 {
@@ -234,7 +241,7 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) {
             if t == _V_NONE {
                 return Node{}, types.ERR_INVALID_CHAR
             }
-            val = newRawNode(self.s[start:self.p], t)
+            val = newRawNode(self.s[start:self.p], t, false)
         } else {
             /* decode the value */
             if val, err = self.Parse(); err != 0 {
@@ -244,7 +251,7 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) {
 
         /* add the value to result */
         // FIXME: ret's address may change here, thus previous referred node in ret may be invalid !!
-        ret.Push(Pair{Key: key, Value: val})
+        ret.Push(NewPair(key, val))
         self.p = self.lspace(self.p)
 
         /* check for EOF */
@@ -291,6 +298,10 @@ func (self *Parser) Pos() int {
     return self.p
 }
 
+
+// Parse returns a ast.Node representing the parser's JSON.
+// NOTICE: the specific parsing lazy dependens parser's option
+// It only parse first layer and first child for Object or Array be default
 func (self *Parser) Parse() (Node, types.ParsingError) {
     switch val := self.decodeValue(); val.Vt {
         case types.V_EOF     : return Node{}, types.ERR_EOF
@@ -299,22 +310,48 @@ func (self *Parser) Parse() (Node, types.ParsingError) {
         case types.V_FALSE   : return falseNode, 0
         case types.V_STRING  : return self.decodeString(val.Iv, val.Ep)
         case types.V_ARRAY:
+            s := self.p - 1;
             if p := skipBlank(self.s, self.p); p >= self.p && self.s[p] == ']' {
                 self.p = p + 1
                 return Node{t: types.V_ARRAY}, 0
             }
             if self.noLazy {
+                if self.loadOnce {
+                    self.noLazy = false
+                }
                 return self.decodeArray(new(linkedNodes))
             }
+            // NOTICE: loadOnce always keep raw json for object or array
+            if self.loadOnce {
+                self.p = s
+                s, e := self.skipFast()
+                if e != 0 {
+                    return Node{}, e
+                }
+                return newRawNode(self.s[s:self.p], types.V_ARRAY, true), 0
+            }
             return newLazyArray(self), 0
         case types.V_OBJECT:
+            s := self.p - 1;
             if p := skipBlank(self.s, self.p); p >= self.p && self.s[p] == '}' {
                 self.p = p + 1
                 return Node{t: types.V_OBJECT}, 0
             }
+            // NOTICE: loadOnce always keep raw json for object or array
             if self.noLazy {
+                if self.loadOnce {
+                    self.noLazy = false
+                }
                 return self.decodeObject(new(linkedPairs))
             }
+            if self.loadOnce {
+                self.p = s
+                s, e := self.skipFast()
+                if e != 0 {
+                    return Node{}, e
+                }
+                return newRawNode(self.s[s:self.p], types.V_OBJECT, true), 0
+            }
             return newLazyObject(self), 0
         case types.V_DOUBLE  : return NewNumber(self.s[val.Ep:self.p]), 0
         case types.V_INTEGER : return NewNumber(self.s[val.Ep:self.p]), 0
@@ -471,7 +508,7 @@ func (self *Node) skipNextNode() *Node {
         if t == _V_NONE {
             return newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))
         }
-        val = newRawNode(parser.s[start:parser.p], t)
+        val = newRawNode(parser.s[start:parser.p], t, false)
     }
 
     /* add the value to result */
@@ -510,7 +547,7 @@ func (self *Node) skipNextPair() (*Pair) {
 
     /* check for EOF */
     if parser.p = parser.lspace(sp); parser.p >= ns {
-        return &Pair{"", *newSyntaxError(parser.syntaxError(types.ERR_EOF))}
+        return newErrorPair(parser.syntaxError(types.ERR_EOF))
     }
 
     /* check for empty object */
@@ -527,7 +564,7 @@ func (self *Node) skipNextPair() (*Pair) {
 
     /* decode the key */
     if njs = parser.decodeValue(); njs.Vt != types.V_STRING {
-        return &Pair{"", *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
+        return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR))
     }
 
     /* extract the key */
@@ -537,34 +574,34 @@ func (self *Node) skipNextPair() (*Pair) {
     /* check for escape sequence */
     if njs.Ep != -1 {
         if key, err = unquote(key); err != 0 {
-            return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
+            return newErrorPair(parser.syntaxError(err))
         }
     }
 
     /* expect a ':' delimiter */
     if err = parser.delim(); err != 0 {
-        return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
+        return newErrorPair(parser.syntaxError(err))
     }
 
     /* skip the value */
     if start, err := parser.skipFast(); err != 0 {
-        return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
+        return newErrorPair(parser.syntaxError(err))
     } else {
         t := switchRawType(parser.s[start])
         if t == _V_NONE {
-            return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
+            return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR))
         }
-        val = newRawNode(parser.s[start:parser.p], t)
+        val = newRawNode(parser.s[start:parser.p], t, false)
     }
 
     /* add the value to result */
-    ret.Push(Pair{Key: key, Value: val})
+    ret.Push(NewPair(key, val))
     self.l++
     parser.p = parser.lspace(parser.p)
 
     /* check for EOF */
     if parser.p >= ns {
-        return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_EOF))}
+        return newErrorPair(parser.syntaxError(types.ERR_EOF))
     }
 
     /* check for the next character */
@@ -577,7 +614,7 @@ func (self *Node) skipNextPair() (*Pair) {
         self.setObject(ret)
         return ret.At(ret.Len()-1)
     default:
-        return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
+        return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR))
     }
 }
 
@@ -658,3 +695,72 @@ func backward(src string, i int) int {
     for ; i>=0 && isSpace(src[i]); i-- {}
     return i
 }
+
+
+func newRawNode(str string, typ types.ValueType, lock bool) Node {
+    ret := Node{
+        t: typ | _V_RAW,
+        p: rt.StrPtr(str),
+        l: uint(len(str)),
+    }
+    if lock {
+        ret.m = new(sync.RWMutex)
+    }
+    return ret
+}
+
+var typeJumpTable = [256]types.ValueType{
+    '"' : types.V_STRING,
+    '-' : _V_NUMBER,
+    '0' : _V_NUMBER,
+    '1' : _V_NUMBER,
+    '2' : _V_NUMBER,
+    '3' : _V_NUMBER,
+    '4' : _V_NUMBER,
+    '5' : _V_NUMBER,
+    '6' : _V_NUMBER,
+    '7' : _V_NUMBER,
+    '8' : _V_NUMBER,
+    '9' : _V_NUMBER,
+    '[' : types.V_ARRAY,
+    'f' : types.V_FALSE,
+    'n' : types.V_NULL,
+    't' : types.V_TRUE,
+    '{' : types.V_OBJECT,
+}
+
+func switchRawType(c byte) types.ValueType {
+    return typeJumpTable[c]
+}
+
+func (self *Node) loadt() types.ValueType {
+    return (types.ValueType)(atomic.LoadInt64(&self.t))
+}
+
+func (self *Node) lock() bool {
+    if m := self.m; m != nil {
+        m.Lock()
+        return true
+    }
+    return false
+}
+
+func (self *Node) unlock() {
+    if m := self.m; m != nil {
+        m.Unlock()
+    }
+}
+
+func (self *Node) rlock() bool {
+    if m := self.m; m != nil {
+        m.RLock()
+        return true
+    }
+    return false
+}
+
+func (self *Node) runlock() {
+    if m := self.m; m != nil {
+        m.RUnlock()
+    }
+}

+ 25 - 6
vendor/github.com/bytedance/sonic/ast/search.go

@@ -21,8 +21,23 @@ import (
     `github.com/bytedance/sonic/internal/native/types`
 )
 
+// SearchOptions controls Searcher's behavior
+type SearchOptions struct {
+    // ValidateJSON indicates the searcher to validate the entire JSON
+    ValidateJSON bool
+
+    // CopyReturn indicates the searcher to copy the result JSON instead of refer from the input
+    // This can help to reduce memory usage if you cache the results
+    CopyReturn bool
+
+    // ConcurrentRead indicates the searcher to return a concurrently-READ-safe node,
+    // including: GetByPath/Get/Index/GetOrIndex/Int64/Bool/Float64/String/Number/Interface/Array/Map/Raw/MarshalJSON
+    ConcurrentRead bool
+}
+
 type Searcher struct {
     parser Parser
+    SearchOptions
 }
 
 func NewSearcher(str string) *Searcher {
@@ -31,12 +46,16 @@ func NewSearcher(str string) *Searcher {
             s:      str,
             noLazy: false,
         },
+        SearchOptions: SearchOptions{
+            ValidateJSON: true,
+        },
     }
 }
 
 // GetByPathCopy search in depth from top json and returns a **Copied** json node at the path location
 func (self *Searcher) GetByPathCopy(path ...interface{}) (Node, error) {
-    return self.getByPath(true, true, path...)
+    self.CopyReturn = true
+    return self.getByPath(path...)
 }
 
 // GetByPathNoCopy search in depth from top json and returns a **Referenced** json node at the path location
@@ -44,15 +63,15 @@ func (self *Searcher) GetByPathCopy(path ...interface{}) (Node, error) {
 // WARN: this search directly refer partial json from top json, which has faster speed,
 // may consumes more memory.
 func (self *Searcher) GetByPath(path ...interface{}) (Node, error) {
-    return self.getByPath(false, true, path...)
+    return self.getByPath(path...)
 }
 
-func (self *Searcher) getByPath(copystring bool, validate bool, path ...interface{}) (Node, error) {
+func (self *Searcher) getByPath(path ...interface{}) (Node, error) {
     var err types.ParsingError
     var start int
 
     self.parser.p = 0
-    start, err = self.parser.getByPath(validate, path...)
+    start, err = self.parser.getByPath(self.ValidateJSON, path...)
     if err != 0 {
         // for compatibility with old version
         if err == types.ERR_NOT_FOUND {
@@ -71,12 +90,12 @@ func (self *Searcher) getByPath(copystring bool, validate bool, path ...interfac
 
     // copy string to reducing memory usage
     var raw string
-    if copystring {
+    if self.CopyReturn {
         raw = rt.Mem2Str([]byte(self.parser.s[start:self.parser.p]))
     } else {
         raw = self.parser.s[start:self.parser.p]
     }
-    return newRawNode(raw, t), nil
+    return newRawNode(raw, t, self.ConcurrentRead), nil
 }
 
 // GetByPath searches a path and returns relaction and types of target

+ 14 - 14
vendor/github.com/bytedance/sonic/ast/b64_amd64.go → vendor/github.com/bytedance/sonic/ast/stubs.go

@@ -1,14 +1,12 @@
-// +build amd64,go1.16
-
-/**
- * Copyright 2023 ByteDance Inc.
- * 
+/*
+ * Copyright 2021 ByteDance Inc.
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *     http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,16 +14,18 @@
  * limitations under the License.
  */
 
-package ast 
+package ast
 
 import (
-    `github.com/cloudwego/base64x`
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/rt"
 )
 
-func decodeBase64(src string) ([]byte, error) {
-    return base64x.StdEncoding.DecodeString(src)
+//go:nosplit
+func mem2ptr(s []byte) unsafe.Pointer {
+    return (*rt.GoSlice)(unsafe.Pointer(&s)).Ptr
 }
 
-func encodeBase64(src []byte) string {
-    return base64x.StdEncoding.EncodeToString(src)
-}
+//go:linkname unquoteBytes encoding/json.unquoteBytes
+func unquoteBytes(s []byte) (t []byte, ok bool)

+ 0 - 55
vendor/github.com/bytedance/sonic/ast/stubs_go115.go

@@ -1,55 +0,0 @@
-// +build !go1.20
-
-/*
- * Copyright 2021 ByteDance Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ast
-
-import (
-    `unsafe`
-    `unicode/utf8`
-
-    `github.com/bytedance/sonic/internal/rt`
-)
-
-//go:noescape
-//go:linkname memmove runtime.memmove
-//goland:noinspection GoUnusedParameter
-func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
-
-//go:linkname unsafe_NewArray reflect.unsafe_NewArray
-//goland:noinspection GoUnusedParameter
-func unsafe_NewArray(typ *rt.GoType, n int) unsafe.Pointer
-
-//go:linkname growslice runtime.growslice
-//goland:noinspection GoUnusedParameter
-func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
-
-//go:nosplit
-func mem2ptr(s []byte) unsafe.Pointer {
-    return (*rt.GoSlice)(unsafe.Pointer(&s)).Ptr
-}
-
-var (
-    //go:linkname safeSet encoding/json.safeSet
-    safeSet [utf8.RuneSelf]bool
-
-    //go:linkname hex encoding/json.hex
-    hex string
-)
-
-//go:linkname unquoteBytes encoding/json.unquoteBytes
-func unquoteBytes(s []byte) (t []byte, ok bool)

+ 0 - 55
vendor/github.com/bytedance/sonic/ast/stubs_go120.go

@@ -1,55 +0,0 @@
-// +build go1.20
-
-/*
- * Copyright 2021 ByteDance Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ast
-
-import (
-    `unsafe`
-    `unicode/utf8`
-
-    `github.com/bytedance/sonic/internal/rt`
-)
-
-//go:noescape
-//go:linkname memmove runtime.memmove
-//goland:noinspection GoUnusedParameter
-func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
-
-//go:linkname unsafe_NewArray reflect.unsafe_NewArray
-//goland:noinspection GoUnusedParameter
-func unsafe_NewArray(typ *rt.GoType, n int) unsafe.Pointer
-
-//go:linkname growslice reflect.growslice
-//goland:noinspection GoUnusedParameter
-func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
-
-//go:nosplit
-func mem2ptr(s []byte) unsafe.Pointer {
-    return (*rt.GoSlice)(unsafe.Pointer(&s)).Ptr
-}
-
-var (
-    //go:linkname safeSet encoding/json.safeSet
-    safeSet [utf8.RuneSelf]bool
-
-    //go:linkname hex encoding/json.hex
-    hex string
-)
-
-//go:linkname unquoteBytes encoding/json.unquoteBytes
-func unquoteBytes(s []byte) (t []byte, ok bool)

+ 31 - 14
vendor/github.com/bytedance/sonic/ast/visitor.go

@@ -18,6 +18,7 @@ package ast
 
 import (
     `encoding/json`
+    `errors`
 
     `github.com/bytedance/sonic/internal/native/types`
 )
@@ -174,6 +175,19 @@ func (self *traverser) decodeArray() error {
     sp := self.parser.p
     ns := len(self.parser.s)
 
+    /* allocate array space and parse every element */
+    if err := self.visitor.OnArrayBegin(_DEFAULT_NODE_CAP); err != nil {
+        if err == VisitOPSkip {
+            // NOTICE: for user needs to skip entiry object
+            self.parser.p -= 1
+            if _, e := self.parser.skipFast(); e != 0 {
+                return e
+            }
+            return self.visitor.OnArrayEnd()
+        }
+        return err
+    }
+
     /* check for EOF */
     self.parser.p = self.parser.lspace(sp)
     if self.parser.p >= ns {
@@ -183,16 +197,9 @@ func (self *traverser) decodeArray() error {
     /* check for empty array */
     if self.parser.s[self.parser.p] == ']' {
         self.parser.p++
-        if err := self.visitor.OnArrayBegin(0); err != nil {
-            return err
-        }
         return self.visitor.OnArrayEnd()
     }
 
-    /* allocate array space and parse every element */
-    if err := self.visitor.OnArrayBegin(_DEFAULT_NODE_CAP); err != nil {
-        return err
-    }
     for {
         /* decode the value */
         if err := self.decodeValue(); err != nil {
@@ -223,6 +230,19 @@ func (self *traverser) decodeObject() error {
     sp := self.parser.p
     ns := len(self.parser.s)
 
+    /* allocate object space and decode each pair */
+    if err := self.visitor.OnObjectBegin(_DEFAULT_NODE_CAP); err != nil {
+        if err == VisitOPSkip {
+            // NOTICE: for user needs to skip entiry object
+            self.parser.p -= 1
+            if _, e := self.parser.skipFast(); e != 0 {
+                return e
+            }
+            return self.visitor.OnObjectEnd()
+        }
+        return err
+    }
+
     /* check for EOF */
     self.parser.p = self.parser.lspace(sp)
     if self.parser.p >= ns {
@@ -232,16 +252,9 @@ func (self *traverser) decodeObject() error {
     /* check for empty object */
     if self.parser.s[self.parser.p] == '}' {
         self.parser.p++
-        if err := self.visitor.OnObjectBegin(0); err != nil {
-            return err
-        }
         return self.visitor.OnObjectEnd()
     }
 
-    /* allocate object space and decode each pair */
-    if err := self.visitor.OnObjectBegin(_DEFAULT_NODE_CAP); err != nil {
-        return err
-    }
     for {
         var njs types.JsonState
         var err types.ParsingError
@@ -313,3 +326,7 @@ func (self *traverser) decodeString(iv int64, ep int) error {
     }
     return self.visitor.OnString(out)
 }
+
+// If visitor return this error on `OnObjectBegin()` or `OnArrayBegin()`,
+// the transverer will skip entiry object or array
+var VisitOPSkip = errors.New("")

+ 3 - 1
vendor/github.com/bytedance/sonic/compat.go

@@ -1,4 +1,4 @@
-// +build !amd64 !go1.16 go1.23
+// +build !amd64,!arm64 go1.25 !go1.17 arm64,!go1.20
 
 /*
  * Copyright 2021 ByteDance Inc.
@@ -27,6 +27,8 @@ import (
     `github.com/bytedance/sonic/option`
 )
 
+const apiKind = UseStdJSON
+
 type frozenConfig struct {
     Config
 }

+ 26 - 19
vendor/github.com/bytedance/sonic/decoder/decoder_compat.go

@@ -1,4 +1,5 @@
-// +build !amd64 !go1.16 go1.23
+//go:build (!amd64 && !arm64) || go1.25 || !go1.17 || (arm64 && !go1.20)
+// +build !amd64,!arm64 go1.25 !go1.17 arm64,!go1.20
 
 /*
 * Copyright 2023 ByteDance Inc.
@@ -19,29 +20,33 @@
 package decoder
 
 import (
-    `bytes`
-    `encoding/json`
-    `io`
-    `reflect`
-    `unsafe`
-
-    `github.com/bytedance/sonic/internal/native/types`
-    `github.com/bytedance/sonic/option`
+	"bytes"
+	"encoding/json"
+	"io"
+	"reflect"
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/decoder/consts"
+	"github.com/bytedance/sonic/internal/native/types"
+	"github.com/bytedance/sonic/option"
+	"github.com/bytedance/sonic/internal/compat"
 )
 
 func init() {
-     println("WARNING: sonic only supports Go1.16~1.22 && CPU amd64, but your environment is not suitable")
+     compat.Warn("sonic/decoder")
 }
 
 const (
-     _F_use_int64       = 0
-     _F_disable_urc     = 2
-     _F_disable_unknown = 3
-     _F_copy_string     = 4
+     _F_use_int64       = consts.F_use_int64
+     _F_disable_urc     = consts.F_disable_unknown
+     _F_disable_unknown = consts.F_disable_unknown
+     _F_copy_string     = consts.F_copy_string
  
-     _F_use_number      = types.B_USE_NUMBER
-     _F_validate_string = types.B_VALIDATE_STRING
-     _F_allow_control   = types.B_ALLOW_CONTROL
+     _F_use_number      = consts.F_use_number
+     _F_validate_string = consts.F_validate_string
+     _F_allow_control   = consts.F_allow_control
+     _F_no_validate_json = consts.F_no_validate_json
+     _F_case_sensitive  = consts.F_case_sensitive
 )
 
 type Options uint64
@@ -53,6 +58,8 @@ const (
      OptionDisableUnknown   Options = 1 << _F_disable_unknown
      OptionCopyString       Options = 1 << _F_copy_string
      OptionValidateString   Options = 1 << _F_validate_string
+     OptionNoValidateJSON   Options = 1 << _F_no_validate_json
+     OptionCaseSensitive    Options = 1 << _F_case_sensitive
 )
 
 func (self *Decoder) SetOptions(opts Options) {
@@ -190,5 +197,5 @@ func (s SyntaxError) Error() string {
      return (*json.SyntaxError)(unsafe.Pointer(&s)).Error()
 }
 
-// MismatchTypeError represents dismatching between json and object
-type MismatchTypeError json.UnmarshalTypeError
+// MismatchTypeError represents mismatching between json and object
+type MismatchTypeError json.UnmarshalTypeError

+ 22 - 18
vendor/github.com/bytedance/sonic/decoder/decoder_amd64.go → vendor/github.com/bytedance/sonic/decoder/decoder_native.go

@@ -1,4 +1,6 @@
-// +build amd64,go1.16,!go1.23
+//go:build (amd64 && go1.17 && !go1.25) || (arm64 && go1.20 && !go1.25)
+// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25
+
 
 /*
 * Copyright 2023 ByteDance Inc.
@@ -19,50 +21,52 @@
 package decoder
 
 import (
-    `github.com/bytedance/sonic/internal/decoder`
+    `github.com/bytedance/sonic/internal/decoder/api`
 )
 
 // Decoder is the decoder context object
-type Decoder = decoder.Decoder
+type Decoder = api.Decoder
 
 // SyntaxError represents json syntax error
-type SyntaxError = decoder.SyntaxError
+type SyntaxError = api.SyntaxError
 
-// MismatchTypeError represents dismatching between json and object
-type MismatchTypeError = decoder.MismatchTypeError
+// MismatchTypeError represents mismatching between json and object
+type MismatchTypeError = api.MismatchTypeError
 
 // Options for decode.
-type Options = decoder.Options
+type Options = api.Options
 
 const (
-    OptionUseInt64         Options = decoder.OptionUseInt64
-    OptionUseNumber        Options = decoder.OptionUseNumber
-    OptionUseUnicodeErrors Options = decoder.OptionUseUnicodeErrors
-    OptionDisableUnknown   Options = decoder.OptionDisableUnknown
-    OptionCopyString       Options = decoder.OptionCopyString
-    OptionValidateString   Options = decoder.OptionValidateString
+    OptionUseInt64         Options = api.OptionUseInt64
+    OptionUseNumber        Options = api.OptionUseNumber
+    OptionUseUnicodeErrors Options = api.OptionUseUnicodeErrors
+    OptionDisableUnknown   Options = api.OptionDisableUnknown
+    OptionCopyString       Options = api.OptionCopyString
+    OptionValidateString   Options = api.OptionValidateString
+    OptionNoValidateJSON   Options = api.OptionNoValidateJSON
+    OptionCaseSensitive    Options = api.OptionCaseSensitive
 )
 
 // StreamDecoder is the decoder context object for streaming input.
-type StreamDecoder = decoder.StreamDecoder
+type StreamDecoder = api.StreamDecoder
 
 var (
     // NewDecoder creates a new decoder instance.
-    NewDecoder = decoder.NewDecoder
+    NewDecoder = api.NewDecoder
 
     // NewStreamDecoder adapts to encoding/json.NewDecoder API.
     //
     // NewStreamDecoder returns a new decoder that reads from r.
-    NewStreamDecoder = decoder.NewStreamDecoder
+    NewStreamDecoder = api.NewStreamDecoder
 
     // Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
     // order to reduce the first-hit latency.
     //
     // Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is
     // a compile option to set the depth of recursive compile for the nested struct type.
-    Pretouch = decoder.Pretouch
+    Pretouch = api.Pretouch
     
     // Skip skips only one json value, and returns first non-blank character position and its ending position if it is valid.
     // Otherwise, returns negative error code using start and invalid character position using end
-    Skip = decoder.Skip
+    Skip = api.Skip
 )

+ 4 - 3
vendor/github.com/bytedance/sonic/encoder/encoder_compat.go

@@ -1,4 +1,4 @@
-// +build !amd64 !go1.16 go1.23
+// +build !amd64,!arm64 go1.25 !go1.17 arm64,!go1.20
 
 /*
 * Copyright 2023 ByteDance Inc.
@@ -19,16 +19,17 @@
 package encoder
 
 import (
-   `io`
+    `io`
     `bytes`
     `encoding/json`
     `reflect`
 
     `github.com/bytedance/sonic/option`
+    `github.com/bytedance/sonic/internal/compat`
 )
 
 func init() {
-    println("WARNING:(encoder) sonic only supports Go1.16~1.22 && CPU amd64, but your environment is not suitable")
+    compat.Warn("sonic/encoder")
 }
 
 // EnableFallback indicates if encoder use fallback

+ 4 - 1
vendor/github.com/bytedance/sonic/encoder/encoder_amd64.go → vendor/github.com/bytedance/sonic/encoder/encoder_native.go

@@ -1,4 +1,4 @@
-// +build amd64,go1.16,!go1.23
+// +build amd64,go1.17,!go1.25 arm64,go1.20,!go1.25
 
 /*
  * Copyright 2023 ByteDance Inc.
@@ -70,6 +70,9 @@ const (
 
     // CompatibleWithStd is used to be compatible with std encoder.
     CompatibleWithStd Options = encoder.CompatibleWithStd
+
+    // Encode Infinity or Nan float into `null`, instead of returning an error.
+    EncodeNullForInfOrNan Options = encoder.EncodeNullForInfOrNan
 )
 
 

+ 7 - 0
vendor/github.com/bytedance/sonic/go.work.sum

@@ -0,0 +1,7 @@
+github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
+github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
+github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
+github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
+github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

+ 2 - 6
vendor/github.com/bytedance/sonic/internal/caching/hashing.go

@@ -23,16 +23,12 @@ import (
 )
 
 var (
-    V_strhash = rt.UnpackEface(strhash)
+    V_strhash = rt.UnpackEface(rt.Strhash)
     S_strhash = *(*uintptr)(V_strhash.Value)
 )
 
-//go:noescape
-//go:linkname strhash runtime.strhash
-func strhash(_ unsafe.Pointer, _ uintptr) uintptr
-
 func StrHash(s string) uint64 {
-    if v := strhash(unsafe.Pointer(&s), 0); v == 0 {
+    if v := rt.Strhash(unsafe.Pointer(&s), 0); v == 0 {
         return 1
     } else {
         return uint64(v)

+ 12 - 0
vendor/github.com/bytedance/sonic/internal/compat/warn.go

@@ -0,0 +1,12 @@
+// +build !amd64,!arm64 go1.25 !go1.17 arm64,!go1.20
+
+package compat
+
+import (
+    "fmt"
+    "os"
+)
+
+func Warn(prefix string) {
+    fmt.Fprintf(os.Stderr, "WARNING: %s only supports (go1.17~1.24 && amd64 CPU) or (go1.20~1.24 && arm64 CPU), but your environment is not suitable and will fallback to encoding/json\n", prefix)
+}

+ 2 - 2
vendor/github.com/bytedance/sonic/internal/cpu/features.go

@@ -24,7 +24,6 @@ import (
 )
 
 var (
-    HasAVX  = cpuid.CPU.Has(cpuid.AVX)
     HasAVX2 = cpuid.CPU.Has(cpuid.AVX2)
     HasSSE = cpuid.CPU.Has(cpuid.SSE)
 )
@@ -33,7 +32,8 @@ func init() {
     switch v := os.Getenv("SONIC_MODE"); v {
         case ""       : break
         case "auto"   : break
-        case "noavx"  : HasAVX = false; fallthrough
+        case "noavx"  : HasAVX2 = false
+        // will also disable avx, act as `noavx`, we remain it to make sure forward compatibility
         case "noavx2" : HasAVX2 = false
         default       : panic(fmt.Sprintf("invalid mode: '%s', should be one of 'auto', 'noavx', 'noavx2'", v))
     }

+ 29 - 109
vendor/github.com/bytedance/sonic/internal/decoder/decoder.go → vendor/github.com/bytedance/sonic/internal/decoder/api/decoder.go

@@ -14,51 +14,54 @@
  * limitations under the License.
  */
 
-package decoder
+package api
 
 import (
-    `unsafe`
-    `encoding/json`
     `reflect`
-    `runtime`
 
     `github.com/bytedance/sonic/internal/native`
     `github.com/bytedance/sonic/internal/native/types`
+	`github.com/bytedance/sonic/internal/decoder/consts`
+	`github.com/bytedance/sonic/internal/decoder/errors`
     `github.com/bytedance/sonic/internal/rt`
     `github.com/bytedance/sonic/option`
-    `github.com/bytedance/sonic/utf8`
 )
 
 const (
-    _F_use_int64       = 0
-    _F_disable_urc     = 2
-    _F_disable_unknown = 3
-    _F_copy_string     = 4
-
-    _F_use_number      = types.B_USE_NUMBER
-    _F_validate_string = types.B_VALIDATE_STRING
-    _F_allow_control   = types.B_ALLOW_CONTROL
+	_F_allow_control = consts.F_allow_control
+	_F_copy_string = consts.F_copy_string
+	_F_disable_unknown = consts.F_disable_unknown
+	_F_disable_urc = consts.F_disable_urc
+	_F_use_int64 = consts.F_use_int64
+	_F_use_number = consts.F_use_number
+	_F_validate_string = consts.F_validate_string
+    _F_case_sensitive = consts.F_case_sensitive
+
+	_MaxStack = consts.MaxStack
+
+	OptionUseInt64 	       = consts.OptionUseInt64
+	OptionUseNumber        = consts.OptionUseNumber
+    OptionUseUnicodeErrors = consts.OptionUseUnicodeErrors
+    OptionDisableUnknown   = consts.OptionDisableUnknown
+    OptionCopyString       = consts.OptionCopyString
+    OptionValidateString   = consts.OptionValidateString
+    OptionNoValidateJSON   = consts.OptionNoValidateJSON
+    OptionCaseSensitive    = consts.OptionCaseSensitive
 )
 
-type Options uint64
-
-const (
-    OptionUseInt64         Options = 1 << _F_use_int64
-    OptionUseNumber        Options = 1 << _F_use_number
-    OptionUseUnicodeErrors Options = 1 << _F_disable_urc
-    OptionDisableUnknown   Options = 1 << _F_disable_unknown
-    OptionCopyString       Options = 1 << _F_copy_string
-    OptionValidateString   Options = 1 << _F_validate_string
+type (
+	Options = consts.Options
+	MismatchTypeError = errors.MismatchTypeError
+	SyntaxError = errors.SyntaxError
 )
 
 func (self *Decoder) SetOptions(opts Options) {
-    if (opts & OptionUseNumber != 0) && (opts & OptionUseInt64 != 0) {
+    if (opts & consts.OptionUseNumber != 0) && (opts & consts.OptionUseInt64 != 0) {
         panic("can't set OptionUseInt64 and OptionUseNumber both!")
     }
     self.f = uint64(opts)
 }
 
-
 // Decoder is the decoder context object
 type Decoder struct {
     i int
@@ -109,44 +112,7 @@ func (self *Decoder) CheckTrailings() error {
 // Decode parses the JSON-encoded data from current position and stores the result
 // in the value pointed to by val.
 func (self *Decoder) Decode(val interface{}) error {
-    /* validate json if needed */
-    if (self.f & (1 << _F_validate_string)) != 0  && !utf8.ValidateString(self.s){
-        dbuf := utf8.CorrectWith(nil, rt.Str2Mem(self.s), "\ufffd")
-        self.s = rt.Mem2Str(dbuf)
-    }
-
-    vv := rt.UnpackEface(val)
-    vp := vv.Value
-
-    /* check for nil type */
-    if vv.Type == nil {
-        return &json.InvalidUnmarshalError{}
-    }
-
-    /* must be a non-nil pointer */
-    if vp == nil || vv.Type.Kind() != reflect.Ptr {
-        return &json.InvalidUnmarshalError{Type: vv.Type.Pack()}
-    }
-
-    etp := rt.PtrElem(vv.Type)
-
-    /* check the defined pointer type for issue 379 */
-    if vv.Type.IsNamed() {
-        newp := vp
-        etp  = vv.Type
-        vp   = unsafe.Pointer(&newp)
-    }
-
-    /* create a new stack, and call the decoder */
-    sb := newStack()
-    nb, err := decodeTypedPointer(self.s, self.i, etp, vp, sb, self.f)
-    /* return the stack back */
-    self.i = nb
-    freeStack(sb)
-
-    /* avoid GC ahead */
-    runtime.KeepAlive(vv)
-    return err
+	return decodeImpl(&self.s, &self.i, self.f, val)
 }
 
 // UseInt64 indicates the Decoder to unmarshal an integer into an interface{} as an
@@ -194,53 +160,7 @@ func (self *Decoder) ValidateString() {
 // Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is
 // a compile option to set the depth of recursive compile for the nested struct type.
 func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
-    cfg := option.DefaultCompileOptions()
-    for _, opt := range opts {
-        opt(&cfg)
-    }
-    return pretouchRec(map[reflect.Type]bool{vt:true}, cfg)
-}
-
-func pretouchType(_vt reflect.Type, opts option.CompileOptions) (map[reflect.Type]bool, error) {
-    /* compile function */
-    compiler := newCompiler().apply(opts)
-    decoder := func(vt *rt.GoType, _ ...interface{}) (interface{}, error) {
-        if pp, err := compiler.compile(_vt); err != nil {
-            return nil, err
-        } else {
-            as := newAssembler(pp)
-            as.name = _vt.String()
-            return as.Load(), nil
-        }
-    }
-
-    /* find or compile */
-    vt := rt.UnpackType(_vt)
-    if val := programCache.Get(vt); val != nil {
-        return nil, nil
-    } else if _, err := programCache.Compute(vt, decoder); err == nil {
-        return compiler.rec, nil
-    } else {
-        return nil, err
-    }
-}
-
-func pretouchRec(vtm map[reflect.Type]bool, opts option.CompileOptions) error {
-    if opts.RecursiveDepth < 0 || len(vtm) == 0 {
-        return nil
-    }
-    next := make(map[reflect.Type]bool)
-    for vt := range(vtm) {
-        sub, err := pretouchType(vt, opts)
-        if err != nil {
-            return err
-        }
-        for svt := range(sub) {
-            next[svt] = true
-        }
-    }
-    opts.RecursiveDepth -= 1
-    return pretouchRec(next, opts)
+	return pretouchImpl(vt, opts...)
 }
 
 // Skip skips only one json value, and returns first non-blank character position and its ending position if it is valid.

+ 38 - 0
vendor/github.com/bytedance/sonic/internal/decoder/api/decoder_amd64.go

@@ -0,0 +1,38 @@
+//go:build go1.17 && !go1.25
+// +build go1.17,!go1.25
+
+/*
+ * Copyright 2021 ByteDance Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package api
+
+import (
+	"github.com/bytedance/sonic/internal/envs"
+	"github.com/bytedance/sonic/internal/decoder/jitdec"
+	"github.com/bytedance/sonic/internal/decoder/optdec"
+)
+
+var (
+	pretouchImpl = jitdec.Pretouch
+	decodeImpl = jitdec.Decode
+) 
+
+ func init() {
+	if envs.UseOptDec {
+		pretouchImpl = optdec.Pretouch
+		decodeImpl = optdec.Decode
+	}
+ }

+ 14 - 12
vendor/github.com/bytedance/sonic/internal/native/avx/u64toa.go → vendor/github.com/bytedance/sonic/internal/decoder/api/decoder_arm64.go

@@ -1,6 +1,4 @@
-// Code generated by Makefile, DO NOT EDIT.
-
-// Code generated by Makefile, DO NOT EDIT.
+// +build go1.17,!go1.25
 
 /*
  * Copyright 2021 ByteDance Inc.
@@ -18,19 +16,23 @@
  * limitations under the License.
  */
 
-package avx
+package api
 
 import (
-    `unsafe`
-
-    `github.com/bytedance/sonic/internal/rt`
+	`github.com/bytedance/sonic/internal/decoder/optdec`
+	`github.com/bytedance/sonic/internal/envs`
 )
 
-var F_u64toa func(out unsafe.Pointer, val uint64) (ret int)
+var (
+	pretouchImpl = optdec.Pretouch
+	decodeImpl = optdec.Decode
+)
 
-var S_u64toa uintptr
 
-//go:nosplit
-func u64toa(out *byte, val uint64) (ret int) {
-    return F_u64toa(rt.NoEscape(unsafe.Pointer(out)), val)
+func init() {
+    // when in aarch64, we enable all optimization
+	envs.EnableOptDec()
+	envs.EnableFastMap()
 }
+
+

+ 9 - 18
vendor/github.com/bytedance/sonic/internal/decoder/stream.go → vendor/github.com/bytedance/sonic/internal/decoder/api/stream.go

@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package decoder
+package api
 
 import (
     `bytes`
@@ -47,6 +47,12 @@ var bufPool = sync.Pool{
     },
 }
 
+func freeBytes(buf []byte) {
+    if rt.CanSizeResue(cap(buf)) {
+        bufPool.Put(buf[:0])
+    }
+}
+
 // NewStreamDecoder adapts to encoding/json.NewDecoder API.
 //
 // NewStreamDecoder returns a new decoder that reads from r.
@@ -61,25 +67,16 @@ func NewStreamDecoder(r io.Reader) *StreamDecoder {
 func (self *StreamDecoder) Decode(val interface{}) (err error) {
     // read more data into buf
     if self.More() {
-        // println(string(self.buf))
         var s = self.scanp
     try_skip:
         var e = len(self.buf)
-        // println("s:", s, "e:", e, "scanned:",self.scanned, "scanp:",self.scanp, self.buf)
         var src = rt.Mem2Str(self.buf[s:e])
-        // if len(src) > 5 {
-        //     println(src[:5], src[len(src)-5:])
-        // } else {
-        //     println(src)
-        // }
         // try skip
         var x = 0;
         if y := native.SkipOneFast(&src, &x); y < 0 {
             if self.readMore()  {
-                // println("more")
                 goto try_skip
             } else {
-                // println("no more")
                 err = SyntaxError{e, self.s, types.ParsingError(-s), ""}
                 self.setErr(err)
                 return
@@ -89,7 +86,6 @@ func (self *StreamDecoder) Decode(val interface{}) (err error) {
             e = x + s
         }
         
-        // println("decode: ", s, e)
         // must copy string here for safety
         self.Decoder.Reset(string(self.buf[s:e]))
         err = self.Decoder.Decode(val)
@@ -101,13 +97,11 @@ func (self *StreamDecoder) Decode(val interface{}) (err error) {
         self.scanp = e
         _, empty := self.scan()
         if empty {
-            // println("recycle")
             // no remain valid bytes, thus we just recycle buffer
             mem := self.buf
             self.buf = nil
-            bufPool.Put(mem[:0])
+            freeBytes(mem)
         } else {
-            // println("keep")
             // remain undecoded bytes, move them onto head
             n := copy(self.buf, self.buf[self.scanp:])
             self.buf = self.buf[:n]
@@ -123,7 +117,6 @@ func (self *StreamDecoder) Decode(val interface{}) (err error) {
 // InputOffset returns the input stream byte offset of the current decoder position. 
 // The offset gives the location of the end of the most recently returned token and the beginning of the next token.
 func (self *StreamDecoder) InputOffset() int64 {
-    // println("input offset",self.scanned, self.scanp)
     return self.scanned + int64(self.scanp)
 }
 
@@ -178,7 +171,7 @@ func (self *StreamDecoder) setErr(err error) {
     self.err = err
     mem := self.buf[:0]
     self.buf = nil
-    bufPool.Put(mem)
+    freeBytes(mem)
 }
 
 func (self *StreamDecoder) peek() (byte, error) {
@@ -237,12 +230,10 @@ func realloc(buf *[]byte) bool {
     l := uint(len(*buf))
     c := uint(cap(*buf))
     if c == 0 {
-        // println("use pool!")
        *buf = bufPool.Get().([]byte)
        return true
     }
     if c - l <= c >> minLeftBufferShift {
-        // println("realloc!")
         e := l+(l>>minLeftBufferShift)
         if e <= c {
             e = c*2

+ 0 - 130
vendor/github.com/bytedance/sonic/internal/decoder/asm_stubs_amd64_go116.go

@@ -1,130 +0,0 @@
-// +build go1.16,!go1.17
-
-// Copyright 2023 CloudWeGo Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package decoder
-
-import (
-    `strconv`
-    _ `unsafe`
-
-    `github.com/bytedance/sonic/internal/jit`
-    `github.com/bytedance/sonic/internal/rt`
-    `github.com/twitchyliquid64/golang-asm/obj`
-    `github.com/twitchyliquid64/golang-asm/obj/x86`
-)
-
-var _runtime_writeBarrier uintptr = rt.GcwbAddr()
-
-//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier
-func gcWriteBarrierAX()
-
-var (
-    _V_writeBarrier = jit.Imm(int64(_runtime_writeBarrier))
-
-    _F_gcWriteBarrierAX = jit.Func(gcWriteBarrierAX)
-)
-
-func (self *_Assembler) WritePtrAX(i int, rec obj.Addr, saveDI bool) {
-    self.Emit("MOVQ", _V_writeBarrier, _R10)
-    self.Emit("CMPL", jit.Ptr(_R10, 0), jit.Imm(0))
-    self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
-    if saveDI {
-        self.save(_DI)
-    }
-    self.Emit("LEAQ", rec, _DI)
-    self.Emit("MOVQ", _F_gcWriteBarrierAX, _R10)  // MOVQ ${fn}, AX
-    self.Rjmp("CALL", _R10)  
-    if saveDI {
-        self.load(_DI)
-    }    
-    self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
-    self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
-    self.Emit("MOVQ", _AX, rec)
-    self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
-}
-
-func (self *_Assembler) WriteRecNotAX(i int, ptr obj.Addr, rec obj.Addr, saveDI bool, saveAX bool) {
-    if rec.Reg == x86.REG_AX || rec.Index == x86.REG_AX {
-        panic("rec contains AX!")
-    }
-    self.Emit("MOVQ", _V_writeBarrier, _R10)
-    self.Emit("CMPL", jit.Ptr(_R10, 0), jit.Imm(0))
-    self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
-    if saveAX {
-        self.Emit("XCHGQ", ptr, _AX)
-    } else {
-        self.Emit("MOVQ", ptr, _AX)
-    }
-    if saveDI {
-        self.save(_DI)
-    }
-    self.Emit("LEAQ", rec, _DI)
-    self.Emit("MOVQ", _F_gcWriteBarrierAX, _R10)  // MOVQ ${fn}, AX
-    self.Rjmp("CALL", _R10)  
-    if saveDI {
-        self.load(_DI)
-    } 
-    if saveAX {
-        self.Emit("XCHGQ", ptr, _AX)
-    }    
-    self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
-    self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
-    self.Emit("MOVQ", ptr, rec)
-    self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
-}
-
-
-func (self *_ValueDecoder) WritePtrAX(i int, rec obj.Addr, saveDI bool) {
-    self.Emit("MOVQ", _V_writeBarrier, _R10)
-    self.Emit("CMPL", jit.Ptr(_R10, 0), jit.Imm(0))
-    self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
-    if saveDI {
-        self.save(_DI)
-    }
-    self.Emit("LEAQ", rec, _DI)
-    self.Emit("MOVQ", _F_gcWriteBarrierAX, _R10)  // MOVQ ${fn}, AX
-    self.Rjmp("CALL", _R10)  
-    if saveDI {
-        self.load(_DI)
-    }    
-    self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
-    self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
-    self.Emit("MOVQ", _AX, rec)
-    self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
-}
-
-func (self *_ValueDecoder) WriteRecNotAX(i int, ptr obj.Addr, rec obj.Addr, saveDI bool) {
-    if rec.Reg == x86.REG_AX || rec.Index == x86.REG_AX {
-        panic("rec contains AX!")
-    }
-    self.Emit("MOVQ", _V_writeBarrier, _R10)
-    self.Emit("CMPL", jit.Ptr(_R10, 0), jit.Imm(0))
-    self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
-    self.Emit("MOVQ", ptr, _AX)
-    if saveDI {
-        self.save(_DI)
-    }
-    self.Emit("LEAQ", rec, _DI)
-    self.Emit("MOVQ", _F_gcWriteBarrierAX, _R10)  // MOVQ ${fn}, AX
-    self.Rjmp("CALL", _R10)  
-    if saveDI {
-        self.load(_DI)
-    }    
-    self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
-    self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
-    self.Emit("MOVQ", ptr, rec)
-    self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
-}

+ 0 - 1950
vendor/github.com/bytedance/sonic/internal/decoder/assembler_stkabi_amd64.go

@@ -1,1950 +0,0 @@
-// +build go1.16,!go1.17
-
-/*
- * Copyright 2021 ByteDance Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package decoder
-
-import (
-    `encoding/json`
-    `fmt`
-    `math`
-    `reflect`
-    `unsafe`
-    
-    `github.com/bytedance/sonic/internal/caching`
-    `github.com/bytedance/sonic/internal/jit`
-    `github.com/bytedance/sonic/internal/native`
-    `github.com/bytedance/sonic/internal/native/types`
-    `github.com/bytedance/sonic/internal/rt`
-    `github.com/twitchyliquid64/golang-asm/obj`
-)
-
-/** Register Allocations
- *
- *  State Registers:
- *
- *      %rbx : stack base
- *      %r12 : input pointer
- *      %r13 : input length
- *      %r14 : input cursor
- *      %r15 : value pointer
- *
- *  Error Registers:
- *
- *      %r10 : error type register
- *      %r11 : error pointer register
- */
-
-/** Function Prototype & Stack Map
- *
- *  func (s string, ic int, vp unsafe.Pointer, sb *_Stack, fv uint64, sv string) (rc int, err error)
- *
- *  s.buf  :   (FP)
- *  s.len  :  8(FP)
- *  ic     : 16(FP)
- *  vp     : 24(FP)
- *  sb     : 32(FP)
- *  fv     : 40(FP)
- *  sv     : 56(FP)
- *  err.vt : 72(FP)
- *  err.vp : 80(FP)
- */
-
-const (
-    _FP_args   = 96     // 96 bytes to pass arguments and return values for this function
-    _FP_fargs  = 80     // 80 bytes for passing arguments to other Go functions
-    _FP_saves  = 40     // 40 bytes for saving the registers before CALL instructions
-    _FP_locals = 144    // 144 bytes for local variables
-)
-
-const (
-    _FP_offs = _FP_fargs + _FP_saves + _FP_locals
-    _FP_size = _FP_offs + 8     // 8 bytes for the parent frame pointer
-    _FP_base = _FP_size + 8     // 8 bytes for the return address
-)
-
-const (
-    _IM_null = 0x6c6c756e   // 'null'
-    _IM_true = 0x65757274   // 'true'
-    _IM_alse = 0x65736c61   // 'alse' ('false' without the 'f')
-)
-
-const (
-    _BM_space = (1 << ' ') | (1 << '\t') | (1 << '\r') | (1 << '\n')
-)
-
-const (
-    _MODE_JSON = 1 << 3 // base64 mode
-)
-
-const (
-    _LB_error           = "_error"
-    _LB_im_error        = "_im_error"
-    _LB_eof_error       = "_eof_error"
-    _LB_type_error      = "_type_error"
-    _LB_field_error     = "_field_error"
-    _LB_range_error     = "_range_error"
-    _LB_stack_error     = "_stack_error"
-    _LB_base64_error    = "_base64_error"
-    _LB_unquote_error   = "_unquote_error"
-    _LB_parsing_error   = "_parsing_error"
-    _LB_parsing_error_v = "_parsing_error_v"
-    _LB_mismatch_error   = "_mismatch_error"
-)
-
-const (
-    _LB_char_0_error  = "_char_0_error"
-    _LB_char_1_error  = "_char_1_error"
-    _LB_char_2_error  = "_char_2_error"
-    _LB_char_3_error  = "_char_3_error"
-    _LB_char_4_error  = "_char_4_error"
-    _LB_char_m2_error = "_char_m2_error"
-    _LB_char_m3_error = "_char_m3_error"
-)
-
-const (
-    _LB_skip_one = "_skip_one"
-    _LB_skip_key_value = "_skip_key_value"
-)
-
-var (
-    _AX = jit.Reg("AX")
-    _CX = jit.Reg("CX")
-    _DX = jit.Reg("DX")
-    _DI = jit.Reg("DI")
-    _SI = jit.Reg("SI")
-    _BP = jit.Reg("BP")
-    _SP = jit.Reg("SP")
-    _R8 = jit.Reg("R8")
-    _R9 = jit.Reg("R9")
-    _X0 = jit.Reg("X0")
-    _X1 = jit.Reg("X1")
-)
-
-var (
-    _ST = jit.Reg("BX")
-    _IP = jit.Reg("R12")
-    _IL = jit.Reg("R13")
-    _IC = jit.Reg("R14")
-    _VP = jit.Reg("R15")
-)
-
-var (
-    _R10 = jit.Reg("R10")    // used for gcWriteBarrier
-    _DF  = jit.Reg("R10")    // reuse R10 in generic decoder for flags
-    _ET  = jit.Reg("R10")
-    _EP  = jit.Reg("R11")
-)
-
-var (
-    _ARG_s  = _ARG_sp
-    _ARG_sp = jit.Ptr(_SP, _FP_base)
-    _ARG_sl = jit.Ptr(_SP, _FP_base + 8)
-    _ARG_ic = jit.Ptr(_SP, _FP_base + 16)
-    _ARG_vp = jit.Ptr(_SP, _FP_base + 24)
-    _ARG_sb = jit.Ptr(_SP, _FP_base + 32)
-    _ARG_fv = jit.Ptr(_SP, _FP_base + 40)
-)
-
-var (
-    _VAR_sv = _VAR_sv_p
-    _VAR_sv_p = jit.Ptr(_SP, _FP_base + 48)
-    _VAR_sv_n = jit.Ptr(_SP, _FP_base + 56)
-    _VAR_vk   = jit.Ptr(_SP, _FP_base + 64)
-)
-
-var (
-    _RET_rc = jit.Ptr(_SP, _FP_base + 72)
-    _RET_et = jit.Ptr(_SP, _FP_base + 80)
-    _RET_ep = jit.Ptr(_SP, _FP_base + 88)
-)
-
-var (
-    _VAR_st = _VAR_st_Vt
-    _VAR_sr = jit.Ptr(_SP, _FP_fargs + _FP_saves)
-)
-
-
-var (
-    _VAR_st_Vt = jit.Ptr(_SP, _FP_fargs + _FP_saves + 0)
-    _VAR_st_Dv = jit.Ptr(_SP, _FP_fargs + _FP_saves + 8)
-    _VAR_st_Iv = jit.Ptr(_SP, _FP_fargs + _FP_saves + 16)
-    _VAR_st_Ep = jit.Ptr(_SP, _FP_fargs + _FP_saves + 24)
-    _VAR_st_Db = jit.Ptr(_SP, _FP_fargs + _FP_saves + 32)
-    _VAR_st_Dc = jit.Ptr(_SP, _FP_fargs + _FP_saves + 40)
-)
-
-var (
-    _VAR_ss_AX = jit.Ptr(_SP, _FP_fargs + _FP_saves + 48)
-    _VAR_ss_CX = jit.Ptr(_SP, _FP_fargs + _FP_saves + 56)
-    _VAR_ss_SI = jit.Ptr(_SP, _FP_fargs + _FP_saves + 64)
-    _VAR_ss_R8 = jit.Ptr(_SP, _FP_fargs + _FP_saves + 72)
-    _VAR_ss_R9 = jit.Ptr(_SP, _FP_fargs + _FP_saves + 80)
-)
-
-var (
-    _VAR_bs_p = jit.Ptr(_SP, _FP_fargs + _FP_saves + 88)
-    _VAR_bs_n = jit.Ptr(_SP, _FP_fargs + _FP_saves + 96)
-    _VAR_bs_LR = jit.Ptr(_SP, _FP_fargs + _FP_saves + 104)
-)
-
-var _VAR_fl = jit.Ptr(_SP, _FP_fargs + _FP_saves + 112)
-
-var (
-    _VAR_et = jit.Ptr(_SP, _FP_fargs + _FP_saves + 120) // save dismatched type
-    _VAR_ic = jit.Ptr(_SP, _FP_fargs + _FP_saves + 128) // save dismatched position
-    _VAR_pc = jit.Ptr(_SP, _FP_fargs + _FP_saves + 136) // save skip return pc
-)
-
-type _Assembler struct {
-    jit.BaseAssembler
-    p _Program
-    name string
-}
-
-func newAssembler(p _Program) *_Assembler {
-    return new(_Assembler).Init(p)
-}
-
-/** Assembler Interface **/
-
-func (self *_Assembler) Load() _Decoder {
-    return ptodec(self.BaseAssembler.Load("decode_"+self.name, _FP_size, _FP_args, argPtrs, localPtrs))
-}
-
-func (self *_Assembler) Init(p _Program) *_Assembler {
-    self.p = p
-    self.BaseAssembler.Init(self.compile)
-    return self
-}
-
-func (self *_Assembler) compile() {
-    self.prologue()
-    self.instrs()
-    self.epilogue()
-    self.copy_string()
-    self.escape_string()
-    self.escape_string_twice()
-    self.skip_one()
-    self.skip_key_value()
-    self.mismatch_error()
-    self.type_error()
-    self.field_error()
-    self.range_error()
-    self.stack_error()
-    self.base64_error()
-    self.parsing_error()
-}
-
-/** Assembler Stages **/
-
-var _OpFuncTab = [256]func(*_Assembler, *_Instr) {
-    _OP_any              : (*_Assembler)._asm_OP_any,
-    _OP_dyn              : (*_Assembler)._asm_OP_dyn,
-    _OP_str              : (*_Assembler)._asm_OP_str,
-    _OP_bin              : (*_Assembler)._asm_OP_bin,
-    _OP_bool             : (*_Assembler)._asm_OP_bool,
-    _OP_num              : (*_Assembler)._asm_OP_num,
-    _OP_i8               : (*_Assembler)._asm_OP_i8,
-    _OP_i16              : (*_Assembler)._asm_OP_i16,
-    _OP_i32              : (*_Assembler)._asm_OP_i32,
-    _OP_i64              : (*_Assembler)._asm_OP_i64,
-    _OP_u8               : (*_Assembler)._asm_OP_u8,
-    _OP_u16              : (*_Assembler)._asm_OP_u16,
-    _OP_u32              : (*_Assembler)._asm_OP_u32,
-    _OP_u64              : (*_Assembler)._asm_OP_u64,
-    _OP_f32              : (*_Assembler)._asm_OP_f32,
-    _OP_f64              : (*_Assembler)._asm_OP_f64,
-    _OP_unquote          : (*_Assembler)._asm_OP_unquote,
-    _OP_nil_1            : (*_Assembler)._asm_OP_nil_1,
-    _OP_nil_2            : (*_Assembler)._asm_OP_nil_2,
-    _OP_nil_3            : (*_Assembler)._asm_OP_nil_3,
-    _OP_deref            : (*_Assembler)._asm_OP_deref,
-    _OP_index            : (*_Assembler)._asm_OP_index,
-    _OP_is_null          : (*_Assembler)._asm_OP_is_null,
-    _OP_is_null_quote    : (*_Assembler)._asm_OP_is_null_quote,
-    _OP_map_init         : (*_Assembler)._asm_OP_map_init,
-    _OP_map_key_i8       : (*_Assembler)._asm_OP_map_key_i8,
-    _OP_map_key_i16      : (*_Assembler)._asm_OP_map_key_i16,
-    _OP_map_key_i32      : (*_Assembler)._asm_OP_map_key_i32,
-    _OP_map_key_i64      : (*_Assembler)._asm_OP_map_key_i64,
-    _OP_map_key_u8       : (*_Assembler)._asm_OP_map_key_u8,
-    _OP_map_key_u16      : (*_Assembler)._asm_OP_map_key_u16,
-    _OP_map_key_u32      : (*_Assembler)._asm_OP_map_key_u32,
-    _OP_map_key_u64      : (*_Assembler)._asm_OP_map_key_u64,
-    _OP_map_key_f32      : (*_Assembler)._asm_OP_map_key_f32,
-    _OP_map_key_f64      : (*_Assembler)._asm_OP_map_key_f64,
-    _OP_map_key_str      : (*_Assembler)._asm_OP_map_key_str,
-    _OP_map_key_utext    : (*_Assembler)._asm_OP_map_key_utext,
-    _OP_map_key_utext_p  : (*_Assembler)._asm_OP_map_key_utext_p,
-    _OP_array_skip       : (*_Assembler)._asm_OP_array_skip,
-    _OP_array_clear      : (*_Assembler)._asm_OP_array_clear,
-    _OP_array_clear_p    : (*_Assembler)._asm_OP_array_clear_p,
-    _OP_slice_init       : (*_Assembler)._asm_OP_slice_init,
-    _OP_slice_append     : (*_Assembler)._asm_OP_slice_append,
-    _OP_object_skip      : (*_Assembler)._asm_OP_object_skip,
-    _OP_object_next      : (*_Assembler)._asm_OP_object_next,
-    _OP_struct_field     : (*_Assembler)._asm_OP_struct_field,
-    _OP_unmarshal        : (*_Assembler)._asm_OP_unmarshal,
-    _OP_unmarshal_p      : (*_Assembler)._asm_OP_unmarshal_p,
-    _OP_unmarshal_text   : (*_Assembler)._asm_OP_unmarshal_text,
-    _OP_unmarshal_text_p : (*_Assembler)._asm_OP_unmarshal_text_p,
-    _OP_lspace           : (*_Assembler)._asm_OP_lspace,
-    _OP_match_char       : (*_Assembler)._asm_OP_match_char,
-    _OP_check_char       : (*_Assembler)._asm_OP_check_char,
-    _OP_load             : (*_Assembler)._asm_OP_load,
-    _OP_save             : (*_Assembler)._asm_OP_save,
-    _OP_drop             : (*_Assembler)._asm_OP_drop,
-    _OP_drop_2           : (*_Assembler)._asm_OP_drop_2,
-    _OP_recurse          : (*_Assembler)._asm_OP_recurse,
-    _OP_goto             : (*_Assembler)._asm_OP_goto,
-    _OP_switch           : (*_Assembler)._asm_OP_switch,
-    _OP_check_char_0     : (*_Assembler)._asm_OP_check_char_0,
-    _OP_dismatch_err     : (*_Assembler)._asm_OP_dismatch_err,
-    _OP_go_skip          : (*_Assembler)._asm_OP_go_skip,
-    _OP_add              : (*_Assembler)._asm_OP_add,
-    _OP_check_empty      : (*_Assembler)._asm_OP_check_empty,
-}
-
-func (self *_Assembler) instr(v *_Instr) {
-    if fn := _OpFuncTab[v.op()]; fn != nil {
-        fn(self, v)
-    } else {
-        panic(fmt.Sprintf("invalid opcode: %d", v.op()))
-    }
-}
-
-func (self *_Assembler) instrs() {
-    for i, v := range self.p {
-        self.Mark(i)
-        self.instr(&v)
-        self.debug_instr(i, &v)
-    }
-}
-
-func (self *_Assembler) epilogue() {
-    self.Mark(len(self.p))
-    self.Emit("XORL", _EP, _EP)                     // XORL EP, EP
-    self.Emit("MOVQ", _VAR_et, _ET)                 // MOVQ VAR_et, ET
-    self.Emit("TESTQ", _ET, _ET)                    // TESTQ ET, ET
-    self.Sjmp("JNZ", _LB_mismatch_error)            // JNZ _LB_mismatch_error
-    self.Link(_LB_error)                            // _error:
-    self.Emit("MOVQ", _IC, _RET_rc)                 // MOVQ IC, rc<>+40(FP)
-    self.Emit("MOVQ", _ET, _RET_et)                 // MOVQ ET, et<>+48(FP)
-    self.Emit("MOVQ", _EP, _RET_ep)                 // MOVQ EP, ep<>+56(FP)
-    self.Emit("MOVQ", jit.Ptr(_SP, _FP_offs), _BP)  // MOVQ _FP_offs(SP), BP
-    self.Emit("ADDQ", jit.Imm(_FP_size), _SP)       // ADDQ $_FP_size, SP
-    self.Emit("RET")                                // RET
-}
-
-func (self *_Assembler) prologue() {
-    self.Emit("SUBQ", jit.Imm(_FP_size), _SP)       // SUBQ $_FP_size, SP
-    self.Emit("MOVQ", _BP, jit.Ptr(_SP, _FP_offs))  // MOVQ BP, _FP_offs(SP)
-    self.Emit("LEAQ", jit.Ptr(_SP, _FP_offs), _BP)  // LEAQ _FP_offs(SP), BP
-    self.Emit("MOVQ", _ARG_sp, _IP)                 // MOVQ s.p<>+0(FP), IP
-    self.Emit("MOVQ", _ARG_sl, _IL)                 // MOVQ s.l<>+8(FP), IL
-    self.Emit("MOVQ", _ARG_ic, _IC)                 // MOVQ ic<>+16(FP), IC
-    self.Emit("MOVQ", _ARG_vp, _VP)                 // MOVQ vp<>+24(FP), VP
-    self.Emit("MOVQ", _ARG_sb, _ST)                 // MOVQ vp<>+32(FP), ST
-    // initialize digital buffer first
-    self.Emit("MOVQ", jit.Imm(_MaxDigitNums), _VAR_st_Dc)    // MOVQ $_MaxDigitNums, ss.Dcap
-    self.Emit("LEAQ", jit.Ptr(_ST, _DbufOffset), _AX)           // LEAQ _DbufOffset(ST), AX
-    self.Emit("MOVQ", _AX, _VAR_st_Db)                          // MOVQ AX, ss.Dbuf
-    self.Emit("XORL", _AX, _AX)                                 // XORL AX, AX
-    self.Emit("MOVQ", _AX, _VAR_et)                          // MOVQ AX, ss.Dp
-}
-
-/** Function Calling Helpers **/
-
-var _REG_go = []obj.Addr {
-    _ST,
-    _VP,
-    _IP,
-    _IL,
-    _IC,
-}
-
-func (self *_Assembler) save(r ...obj.Addr) {
-    for i, v := range r {
-        if i > _FP_saves / 8 - 1 {
-            panic("too many registers to save")
-        } else {
-            self.Emit("MOVQ", v, jit.Ptr(_SP, _FP_fargs + int64(i) * 8))
-        }
-    }
-}
-
-func (self *_Assembler) load(r ...obj.Addr) {
-    for i, v := range r {
-        if i > _FP_saves / 8 - 1 {
-            panic("too many registers to load")
-        } else {
-            self.Emit("MOVQ", jit.Ptr(_SP, _FP_fargs + int64(i) * 8), v)
-        }
-    }
-}
-
-func (self *_Assembler) call(fn obj.Addr) {
-    self.Emit("MOVQ", fn, _AX)  // MOVQ ${fn}, AX
-    self.Rjmp("CALL", _AX)      // CALL AX
-}
-
-func (self *_Assembler) call_go(fn obj.Addr) {
-    self.save(_REG_go...)   // SAVE $REG_go
-    self.call(fn)           // CALL ${fn}
-    self.load(_REG_go...)   // LOAD $REG_go
-}
-
-func (self *_Assembler) call_sf(fn obj.Addr) {
-    self.Emit("LEAQ", _ARG_s, _DI)                      // LEAQ s<>+0(FP), DI
-    self.Emit("MOVQ", _IC, _ARG_ic)                     // MOVQ IC, ic<>+16(FP)
-    self.Emit("LEAQ", _ARG_ic, _SI)                     // LEAQ ic<>+16(FP), SI
-    self.Emit("LEAQ", jit.Ptr(_ST, _FsmOffset), _DX)    // LEAQ _FsmOffset(ST), DX
-    self.Emit("MOVQ", _ARG_fv, _CX)
-    self.call(fn)                                       // CALL ${fn}
-    self.Emit("MOVQ", _ARG_ic, _IC)                     // MOVQ ic<>+16(FP), IC
-}
-
-func (self *_Assembler) call_vf(fn obj.Addr) {
-    self.Emit("LEAQ", _ARG_s, _DI)      // LEAQ s<>+0(FP), DI
-    self.Emit("MOVQ", _IC, _ARG_ic)     // MOVQ IC, ic<>+16(FP)
-    self.Emit("LEAQ", _ARG_ic, _SI)     // LEAQ ic<>+16(FP), SI
-    self.Emit("LEAQ", _VAR_st, _DX)     // LEAQ st, DX
-    self.call(fn)                       // CALL ${fn}
-    self.Emit("MOVQ", _ARG_ic, _IC)     // MOVQ ic<>+16(FP), IC
-}
-
-/** Assembler Error Handlers **/
-
-var (
-    _F_convT64        = jit.Func(convT64)
-    _F_error_wrap     = jit.Func(error_wrap)
-    _F_error_type     = jit.Func(error_type)
-    _F_error_field    = jit.Func(error_field)
-    _F_error_value    = jit.Func(error_value)
-    _F_error_mismatch = jit.Func(error_mismatch)
-)
-
-var (
-    _I_int8    , _T_int8    = rtype(reflect.TypeOf(int8(0)))
-    _I_int16   , _T_int16   = rtype(reflect.TypeOf(int16(0)))
-    _I_int32   , _T_int32   = rtype(reflect.TypeOf(int32(0)))
-    _I_uint8   , _T_uint8   = rtype(reflect.TypeOf(uint8(0)))
-    _I_uint16  , _T_uint16  = rtype(reflect.TypeOf(uint16(0)))
-    _I_uint32  , _T_uint32  = rtype(reflect.TypeOf(uint32(0)))
-    _I_float32 , _T_float32 = rtype(reflect.TypeOf(float32(0)))
-)
-
-var (
-    _T_error                    = rt.UnpackType(errorType)
-    _I_base64_CorruptInputError = jit.Itab(_T_error, base64CorruptInputError)
-)
-
-var (
-    _V_stackOverflow              = jit.Imm(int64(uintptr(unsafe.Pointer(&stackOverflow))))
-    _I_json_UnsupportedValueError = jit.Itab(_T_error, reflect.TypeOf(new(json.UnsupportedValueError)))
-    _I_json_MismatchTypeError     = jit.Itab(_T_error, reflect.TypeOf(new(MismatchTypeError)))
-)
-
-func (self *_Assembler) type_error() {
-    self.Link(_LB_type_error)                   // _type_error:
-    self.Emit("MOVQ", _ET, jit.Ptr(_SP, 0))     // MOVQ    ET, (SP)
-    self.call_go(_F_error_type)                 // CALL_GO error_type
-    self.Emit("MOVQ", jit.Ptr(_SP, 8), _ET)     // MOVQ    8(SP), ET
-    self.Emit("MOVQ", jit.Ptr(_SP, 16), _EP)    // MOVQ    16(SP), EP
-    self.Sjmp("JMP" , _LB_error)                // JMP     _error
-}
-
-
-func (self *_Assembler) mismatch_error() {
-    self.Link(_LB_mismatch_error)                     // _type_error:
-    self.Emit("MOVQ", _VAR_et, _ET)                   // MOVQ _VAR_et, ET
-    self.Emit("MOVQ", _VAR_ic, _EP)                   // MOVQ _VAR_ic, EP
-    self.Emit("MOVQ", _I_json_MismatchTypeError, _AX) // MOVQ _I_json_MismatchTypeError, AX
-    self.Emit("CMPQ", _ET, _AX)                       // CMPQ ET, AX
-    self.Sjmp("JE"  , _LB_error)                      // JE _LB_error
-    self.Emit("MOVQ", _ARG_sp, _AX)
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0))     // MOVQ    AX, (SP)
-    self.Emit("MOVQ", _ARG_sl, _CX)
-    self.Emit("MOVQ", _CX, jit.Ptr(_SP, 8))     // MOVQ    CX, 8(SP)
-    self.Emit("MOVQ", _VAR_ic, _AX)
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 16))    // MOVQ    AX, 16(SP)
-    self.Emit("MOVQ", _VAR_et, _CX)
-    self.Emit("MOVQ", _CX, jit.Ptr(_SP, 24))    // MOVQ    CX, 24(SP)
-    self.call_go(_F_error_mismatch)             // CALL_GO error_type
-    self.Emit("MOVQ", jit.Ptr(_SP, 32), _ET)    // MOVQ    32(SP), ET
-    self.Emit("MOVQ", jit.Ptr(_SP, 40), _EP)    // MOVQ    40(SP), EP
-    self.Sjmp("JMP" , _LB_error)                // JMP     _error
-}
-
-func (self *_Assembler) _asm_OP_dismatch_err(p *_Instr) {
-    self.Emit("MOVQ", _IC, _VAR_ic)  
-    self.Emit("MOVQ", jit.Type(p.vt()), _ET)       
-    self.Emit("MOVQ", _ET, _VAR_et)
-}
-
-func (self *_Assembler) _asm_OP_go_skip(p *_Instr) {
-    self.Byte(0x4c, 0x8d, 0x0d)         // LEAQ (PC), R9
-    self.Xref(p.vi(), 4)
-    self.Emit("MOVQ", _R9, _VAR_pc)
-    self.Sjmp("JMP"  , _LB_skip_one)            // JMP     _skip_one
-}
-
-func (self *_Assembler) skip_one() {
-    self.Link(_LB_skip_one)                     // _skip:
-    self.Emit("MOVQ", _VAR_ic, _IC)             // MOVQ    _VAR_ic, IC
-    self.call_sf(_F_skip_one)                   // CALL_SF skip_one
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ   AX, AX
-    self.Sjmp("JS"   , _LB_parsing_error_v)     // JS      _parse_error_v
-    self.Emit("MOVQ" , _VAR_pc, _R9)            // MOVQ    pc, R9
-    self.Rjmp("JMP"  , _R9)                     // JMP     (R9)
-}
-
-
-func (self *_Assembler) skip_key_value() {
-    self.Link(_LB_skip_key_value)               // _skip:
-    // skip the key
-    self.Emit("MOVQ", _VAR_ic, _IC)             // MOVQ    _VAR_ic, IC
-    self.call_sf(_F_skip_one)                   // CALL_SF skip_one
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ   AX, AX
-    self.Sjmp("JS"   , _LB_parsing_error_v)     // JS      _parse_error_v
-    // match char ':'
-    self.lspace("_global_1")
-    self.Emit("CMPB", jit.Sib(_IP, _IC, 1, 0), jit.Imm(':'))
-    self.Sjmp("JNE"  , _LB_parsing_error_v)     // JNE     _parse_error_v
-    self.Emit("ADDQ", jit.Imm(1), _IC)          // ADDQ    $1, IC
-    self.lspace("_global_2")
-    // skip the value
-    self.call_sf(_F_skip_one)                   // CALL_SF skip_one
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ   AX, AX
-    self.Sjmp("JS"   , _LB_parsing_error_v)     // JS      _parse_error_v
-    // jump back to specified address
-    self.Emit("MOVQ" , _VAR_pc, _R9)            // MOVQ    pc, R9
-    self.Rjmp("JMP"  , _R9)                     // JMP     (R9)
-}
-
-func (self *_Assembler) field_error() {
-    self.Link(_LB_field_error)                  // _field_error:
-    self.Emit("MOVOU", _VAR_sv, _X0)            // MOVOU   sv, X0
-    self.Emit("MOVOU", _X0, jit.Ptr(_SP, 0))    // MOVOU   X0, (SP)
-    self.call_go(_F_error_field)                // CALL_GO error_field
-    self.Emit("MOVQ" , jit.Ptr(_SP, 16), _ET)   // MOVQ    16(SP), ET
-    self.Emit("MOVQ" , jit.Ptr(_SP, 24), _EP)   // MOVQ    24(SP), EP
-    self.Sjmp("JMP"  , _LB_error)               // JMP     _error
-}
-
-func (self *_Assembler) range_error() {
-    self.Link(_LB_range_error)                  // _range_error:
-    self.slice_from(_VAR_st_Ep, 0)              // SLICE   st.Ep, $0
-    self.Emit("MOVQ", _DI, jit.Ptr(_SP, 0))     // MOVQ    DI, (SP)
-    self.Emit("MOVQ", _SI, jit.Ptr(_SP, 8))     // MOVQ    SI, 8(SP)
-    self.Emit("MOVQ", _ET, jit.Ptr(_SP, 16))    // MOVQ    ET, 16(SP)
-    self.Emit("MOVQ", _EP, jit.Ptr(_SP, 24))    // MOVQ    EP, 24(SP)
-    self.call_go(_F_error_value)                // CALL_GO error_value
-    self.Emit("MOVQ", jit.Ptr(_SP, 32), _ET)    // MOVQ    32(SP), ET
-    self.Emit("MOVQ", jit.Ptr(_SP, 40), _EP)    // MOVQ    40(SP), EP
-    self.Sjmp("JMP" , _LB_error)                // JMP     _error
-}
-
-func (self *_Assembler) stack_error() {
-    self.Link(_LB_stack_error)                              // _stack_error:
-    self.Emit("MOVQ", _V_stackOverflow, _EP)                // MOVQ ${_V_stackOverflow}, EP
-    self.Emit("MOVQ", _I_json_UnsupportedValueError, _ET)   // MOVQ ${_I_json_UnsupportedValueError}, ET
-    self.Sjmp("JMP" , _LB_error)                            // JMP  _error
-}
-
-func (self *_Assembler) base64_error() {
-    self.Link(_LB_base64_error)
-    self.Emit("NEGQ", _AX)                                  // NEGQ    AX
-    self.Emit("SUBQ", jit.Imm(1), _AX)                      // SUBQ    $1, AX
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0))                 // MOVQ    AX, (SP)
-    self.call_go(_F_convT64)                                // CALL_GO convT64
-    self.Emit("MOVQ", jit.Ptr(_SP, 8), _EP)                 // MOVQ    8(SP), EP
-    self.Emit("MOVQ", _I_base64_CorruptInputError, _ET)     // MOVQ    ${itab(base64.CorruptInputError)}, ET
-    self.Sjmp("JMP" , _LB_error)                            // JMP     _error
-}
-
-func (self *_Assembler) parsing_error() {
-    self.Link(_LB_eof_error)                                            // _eof_error:
-    self.Emit("MOVQ" , _IL, _IC)                                        // MOVQ    IL, IC
-    self.Emit("MOVL" , jit.Imm(int64(types.ERR_EOF)), _EP)              // MOVL    ${types.ERR_EOF}, EP
-    self.Sjmp("JMP"  , _LB_parsing_error)                               // JMP     _parsing_error
-    self.Link(_LB_unquote_error)                                        // _unquote_error:
-    self.Emit("SUBQ" , _VAR_sr, _SI)                                    // SUBQ    sr, SI
-    self.Emit("SUBQ" , _SI, _IC)                                        // SUBQ    IL, IC
-    self.Link(_LB_parsing_error_v)                                      // _parsing_error_v:
-    self.Emit("MOVQ" , _AX, _EP)                                        // MOVQ    AX, EP
-    self.Emit("NEGQ" , _EP)                                             // NEGQ    EP
-    self.Sjmp("JMP"  , _LB_parsing_error)                               // JMP     _parsing_error
-    self.Link(_LB_char_m3_error)                                        // _char_m3_error:
-    self.Emit("SUBQ" , jit.Imm(1), _IC)                                 // SUBQ    $1, IC
-    self.Link(_LB_char_m2_error)                                        // _char_m2_error:
-    self.Emit("SUBQ" , jit.Imm(2), _IC)                                 // SUBQ    $2, IC
-    self.Sjmp("JMP"  , _LB_char_0_error)                                // JMP     _char_0_error
-    self.Link(_LB_im_error)                                             // _im_error:
-    self.Emit("CMPB" , _CX, jit.Sib(_IP, _IC, 1, 0))                    // CMPB    CX, (IP)(IC)
-    self.Sjmp("JNE"  , _LB_char_0_error)                                // JNE     _char_0_error
-    self.Emit("SHRL" , jit.Imm(8), _CX)                                 // SHRL    $8, CX
-    self.Emit("CMPB" , _CX, jit.Sib(_IP, _IC, 1, 1))                    // CMPB    CX, 1(IP)(IC)
-    self.Sjmp("JNE"  , _LB_char_1_error)                                // JNE     _char_1_error
-    self.Emit("SHRL" , jit.Imm(8), _CX)                                 // SHRL    $8, CX
-    self.Emit("CMPB" , _CX, jit.Sib(_IP, _IC, 1, 2))                    // CMPB    CX, 2(IP)(IC)
-    self.Sjmp("JNE"  , _LB_char_2_error)                                // JNE     _char_2_error
-    self.Sjmp("JMP"  , _LB_char_3_error)                                // JNE     _char_3_error
-    self.Link(_LB_char_4_error)                                         // _char_4_error:
-    self.Emit("ADDQ" , jit.Imm(1), _IC)                                 // ADDQ    $1, IC
-    self.Link(_LB_char_3_error)                                         // _char_3_error:
-    self.Emit("ADDQ" , jit.Imm(1), _IC)                                 // ADDQ    $1, IC
-    self.Link(_LB_char_2_error)                                         // _char_2_error:
-    self.Emit("ADDQ" , jit.Imm(1), _IC)                                 // ADDQ    $1, IC
-    self.Link(_LB_char_1_error)                                         // _char_1_error:
-    self.Emit("ADDQ" , jit.Imm(1), _IC)                                 // ADDQ    $1, IC
-    self.Link(_LB_char_0_error)                                         // _char_0_error:
-    self.Emit("MOVL" , jit.Imm(int64(types.ERR_INVALID_CHAR)), _EP)     // MOVL    ${types.ERR_INVALID_CHAR}, EP
-    self.Link(_LB_parsing_error)                                        // _parsing_error:
-    self.Emit("MOVOU", _ARG_s, _X0)                                     // MOVOU   s, X0
-    self.Emit("MOVOU", _X0, jit.Ptr(_SP, 0))                            // MOVOU   X0, (SP)
-    self.Emit("MOVQ" , _IC, jit.Ptr(_SP, 16))                           // MOVQ    IC, 16(SP)
-    self.Emit("MOVQ" , _EP, jit.Ptr(_SP, 24))                           // MOVQ    EP, 24(SP)
-    self.call_go(_F_error_wrap)                                         // CALL_GO error_wrap
-    self.Emit("MOVQ" , jit.Ptr(_SP, 32), _ET)                           // MOVQ    32(SP), ET
-    self.Emit("MOVQ" , jit.Ptr(_SP, 40), _EP)                           // MOVQ    40(SP), EP
-    self.Sjmp("JMP"  , _LB_error)                                       // JMP     _error
-}
-
-/** Memory Management Routines **/
-
-var (
-    _T_byte     = jit.Type(byteType)
-    _F_mallocgc = jit.Func(mallocgc)
-)
-
-func (self *_Assembler) malloc(nb obj.Addr, ret obj.Addr) {
-    self.Emit("XORL", _AX, _AX)                 // XORL    AX, AX
-    self.Emit("MOVQ", _T_byte, _CX)             // MOVQ    ${type(byte)}, CX
-    self.Emit("MOVQ", nb, jit.Ptr(_SP, 0))      // MOVQ    ${nb}, (SP)
-    self.Emit("MOVQ", _CX, jit.Ptr(_SP, 8))     // MOVQ    CX, 8(SP)
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 16))    // MOVQ    AX, 16(SP)
-    self.call_go(_F_mallocgc)                   // CALL_GO mallocgc
-    self.Emit("MOVQ", jit.Ptr(_SP, 24), ret)    // MOVQ    24(SP), ${ret}
-}
-
-func (self *_Assembler) valloc(vt reflect.Type, ret obj.Addr) {
-    self.Emit("MOVQ", jit.Imm(int64(vt.Size())), _AX)   // MOVQ    ${vt.Size()}, AX
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0))             // MOVQ    AX, (SP)
-    self.Emit("MOVQ", jit.Type(vt), _AX)                // MOVQ    ${vt}, AX
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 8))             // MOVQ    AX, 8(SP)
-    self.Emit("MOVB", jit.Imm(1), jit.Ptr(_SP, 16))     // MOVB    $1, 16(SP)
-    self.call_go(_F_mallocgc)                           // CALL_GO mallocgc
-    self.Emit("MOVQ", jit.Ptr(_SP, 24), ret)            // MOVQ    24(SP), ${ret}
-}
-
-func (self *_Assembler) vfollow(vt reflect.Type) {
-    self.Emit("MOVQ" , jit.Ptr(_VP, 0), _AX)    // MOVQ   (VP), AX
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ  AX, AX
-    self.Sjmp("JNZ"  , "_end_{n}")              // JNZ    _end_{n}
-    self.valloc(vt, _AX)                        // VALLOC ${vt}, AX
-    self.WritePtrAX(1, jit.Ptr(_VP, 0), false)    // MOVQ   AX, (VP)
-    self.Link("_end_{n}")                       // _end_{n}:
-    self.Emit("MOVQ" , _AX, _VP)                // MOVQ   AX, VP
-}
-
-/** Value Parsing Routines **/
-
-var (
-    _F_vstring   = jit.Imm(int64(native.S_vstring))
-    _F_vnumber   = jit.Imm(int64(native.S_vnumber))
-    _F_vsigned   = jit.Imm(int64(native.S_vsigned))
-    _F_vunsigned = jit.Imm(int64(native.S_vunsigned))
-)
-
-func (self *_Assembler) check_err(vt reflect.Type, pin string, pin2 int) {
-    self.Emit("MOVQ" , _VAR_st_Vt, _AX)         // MOVQ st.Vt, AX
-    self.Emit("TESTQ", _AX, _AX)                // CMPQ AX, ${native.V_STRING}
-    // try to skip the value
-    if vt != nil {
-        self.Sjmp("JNS" , "_check_err_{n}")        // JNE  _parsing_error_v
-        self.Emit("MOVQ", jit.Type(vt), _ET)         
-        self.Emit("MOVQ", _ET, _VAR_et)
-        if pin2 != -1 {
-            self.Emit("SUBQ", jit.Imm(1), _BP)
-            self.Emit("MOVQ", _BP, _VAR_ic)
-            self.Byte(0x4c  , 0x8d, 0x0d)         // LEAQ (PC), R9
-            self.Xref(pin2, 4)
-            self.Emit("MOVQ", _R9, _VAR_pc)
-            self.Sjmp("JMP" , _LB_skip_key_value)
-        } else {
-            self.Emit("MOVQ", _BP, _VAR_ic)
-            self.Byte(0x4c  , 0x8d, 0x0d)         // LEAQ (PC), R9
-            self.Sref(pin, 4)
-            self.Emit("MOVQ", _R9, _VAR_pc)
-            self.Sjmp("JMP" , _LB_skip_one)
-        }
-        self.Link("_check_err_{n}")
-    } else {
-        self.Sjmp("JS"   , _LB_parsing_error_v)     // JNE  _parsing_error_v
-    }
-}
-
-func (self *_Assembler) check_eof(d int64) {
-    if d == 1 {
-        self.Emit("CMPQ", _IC, _IL)         // CMPQ IC, IL
-        self.Sjmp("JAE" , _LB_eof_error)    // JAE  _eof_error
-    } else {
-        self.Emit("LEAQ", jit.Ptr(_IC, d), _AX)     // LEAQ ${d}(IC), AX
-        self.Emit("CMPQ", _AX, _IL)                 // CMPQ AX, IL
-        self.Sjmp("JA"  , _LB_eof_error)            // JA   _eof_error
-    }
-}
-
-func (self *_Assembler) parse_string() {    // parse_string has a validate flag params in the last
-    self.Emit("MOVQ", _ARG_fv, _CX)
-    self.call_vf(_F_vstring)
-    self.check_err(nil, "", -1)
-}
-
-func (self *_Assembler) parse_number(vt reflect.Type, pin string, pin2 int) {
-    self.Emit("MOVQ", _IC, _BP)
-    self.call_vf(_F_vnumber)                               // call  vnumber
-    self.check_err(vt, pin, pin2)
-}
-
-func (self *_Assembler) parse_signed(vt reflect.Type, pin string, pin2 int) {
-    self.Emit("MOVQ", _IC, _BP)
-    self.call_vf(_F_vsigned)
-    self.check_err(vt, pin, pin2)
-}
-
-func (self *_Assembler) parse_unsigned(vt reflect.Type, pin string, pin2 int) {
-    self.Emit("MOVQ", _IC, _BP)
-    self.call_vf(_F_vunsigned)
-    self.check_err(vt, pin, pin2)
-}
-
-// Pointer: DI, Size: SI, Return: R9  
-func (self *_Assembler) copy_string() {
-    self.Link("_copy_string")
-    self.Emit("MOVQ", _DI, _VAR_bs_p)
-    self.Emit("MOVQ", _SI, _VAR_bs_n)
-    self.Emit("MOVQ", _R9, _VAR_bs_LR)
-    self.malloc(_SI, _AX)                              
-    self.Emit("MOVQ", _AX, _VAR_sv_p)                    
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0))                    
-    self.Emit("MOVQ", _VAR_bs_p, _DI)
-    self.Emit("MOVQ", _DI, jit.Ptr(_SP, 8))
-    self.Emit("MOVQ", _VAR_bs_n, _SI)
-    self.Emit("MOVQ", _SI, jit.Ptr(_SP, 16))
-    self.call_go(_F_memmove)
-    self.Emit("MOVQ", _VAR_sv_p, _DI)
-    self.Emit("MOVQ", _VAR_bs_n, _SI)
-    self.Emit("MOVQ", _VAR_bs_LR, _R9)
-    self.Rjmp("JMP", _R9)
-}
-
-// Pointer: DI, Size: SI, Return: R9
-func (self *_Assembler) escape_string() {
-    self.Link("_escape_string")
-    self.Emit("MOVQ" , _DI, _VAR_bs_p)
-    self.Emit("MOVQ" , _SI, _VAR_bs_n)
-    self.Emit("MOVQ" , _R9, _VAR_bs_LR)
-    self.malloc(_SI, _DX)                                    // MALLOC SI, DX
-    self.Emit("MOVQ" , _DX, _VAR_sv_p)
-    self.Emit("MOVQ" , _VAR_bs_p, _DI)
-    self.Emit("MOVQ" , _VAR_bs_n, _SI)                                  
-    self.Emit("LEAQ" , _VAR_sr, _CX)                            // LEAQ   sr, CX
-    self.Emit("XORL" , _R8, _R8)                                // XORL   R8, R8
-    self.Emit("BTQ"  , jit.Imm(_F_disable_urc), _ARG_fv)        // BTQ    ${_F_disable_urc}, fv
-    self.Emit("SETCC", _R8)                                     // SETCC  R8
-    self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _R8)   // SHLQ   ${types.B_UNICODE_REPLACE}, R8
-    self.call(_F_unquote)                                       // CALL   unquote
-    self.Emit("MOVQ" , _VAR_bs_n, _SI)                                  // MOVQ   ${n}, SI
-    self.Emit("ADDQ" , jit.Imm(1), _SI)                         // ADDQ   $1, SI
-    self.Emit("TESTQ", _AX, _AX)                                // TESTQ  AX, AX
-    self.Sjmp("JS"   , _LB_unquote_error)                       // JS     _unquote_error
-    self.Emit("MOVQ" , _AX, _SI)
-    self.Emit("MOVQ" , _VAR_sv_p, _DI)
-    self.Emit("MOVQ" , _VAR_bs_LR, _R9)
-    self.Rjmp("JMP", _R9)
-}
-
-func (self *_Assembler) escape_string_twice() {
-    self.Link("_escape_string_twice")
-    self.Emit("MOVQ" , _DI, _VAR_bs_p)
-    self.Emit("MOVQ" , _SI, _VAR_bs_n)
-    self.Emit("MOVQ" , _R9, _VAR_bs_LR)
-    self.malloc(_SI, _DX)                                        // MALLOC SI, DX
-    self.Emit("MOVQ" , _DX, _VAR_sv_p)
-    self.Emit("MOVQ" , _VAR_bs_p, _DI)
-    self.Emit("MOVQ" , _VAR_bs_n, _SI)        
-    self.Emit("LEAQ" , _VAR_sr, _CX)                                // LEAQ   sr, CX
-    self.Emit("MOVL" , jit.Imm(types.F_DOUBLE_UNQUOTE), _R8)        // MOVL   ${types.F_DOUBLE_UNQUOTE}, R8
-    self.Emit("BTQ"  , jit.Imm(_F_disable_urc), _ARG_fv)            // BTQ    ${_F_disable_urc}, AX
-    self.Emit("XORL" , _AX, _AX)                                    // XORL   AX, AX
-    self.Emit("SETCC", _AX)                                         // SETCC  AX
-    self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _AX)       // SHLQ   ${types.B_UNICODE_REPLACE}, AX
-    self.Emit("ORQ"  , _AX, _R8)                                    // ORQ    AX, R8
-    self.call(_F_unquote)                                           // CALL   unquote
-    self.Emit("MOVQ" , _VAR_bs_n, _SI)                              // MOVQ   ${n}, SI
-    self.Emit("ADDQ" , jit.Imm(3), _SI)                             // ADDQ   $3, SI
-    self.Emit("TESTQ", _AX, _AX)                                    // TESTQ  AX, AX
-    self.Sjmp("JS"   , _LB_unquote_error)                           // JS     _unquote_error
-    self.Emit("MOVQ" , _AX, _SI)
-    self.Emit("MOVQ" , _VAR_sv_p, _DI)
-    self.Emit("MOVQ" , _VAR_bs_LR, _R9)
-    self.Rjmp("JMP", _R9)
-}
-
-/** Range Checking Routines **/
-
-var (
-    _V_max_f32 = jit.Imm(int64(uintptr(unsafe.Pointer(_Vp_max_f32))))
-    _V_min_f32 = jit.Imm(int64(uintptr(unsafe.Pointer(_Vp_min_f32))))
-)
-
-var (
-    _Vp_max_f32 = new(float32)
-    _Vp_min_f32 = new(float32)
-)
-
-func init() {
-    *_Vp_max_f32 = math.MaxFloat32
-    *_Vp_min_f32 = -math.MaxFloat32
-}
-
-func (self *_Assembler) range_single() {
-    self.Emit("CVTSD2SS", _VAR_st_Dv, _X0)              // CVTSD2SS st.Dv, X0
-    self.Emit("MOVQ"    , _V_max_f32, _AX)              // MOVQ     _max_f32, AX
-    self.Emit("MOVQ"    , jit.Gitab(_I_float32), _ET)   // MOVQ     ${itab(float32)}, ET
-    self.Emit("MOVQ"    , jit.Gtype(_T_float32), _EP)   // MOVQ     ${type(float32)}, EP
-    self.Emit("UCOMISS" , jit.Ptr(_AX, 0), _X0)         // UCOMISS  (AX), X0
-    self.Sjmp("JA"      , _LB_range_error)              // JA       _range_error
-    self.Emit("MOVQ"    , _V_min_f32, _AX)              // MOVQ     _min_f32, AX
-    self.Emit("UCOMISS" , jit.Ptr(_AX, 0), _X0)         // UCOMISS  (AX), X0
-    self.Sjmp("JB"      , _LB_range_error)              // JB       _range_error
-}
-
-func (self *_Assembler) range_signed(i *rt.GoItab, t *rt.GoType, a int64, b int64) {
-    self.Emit("MOVQ", _VAR_st_Iv, _AX)      // MOVQ st.Iv, AX
-    self.Emit("MOVQ", jit.Gitab(i), _ET)    // MOVQ ${i}, ET
-    self.Emit("MOVQ", jit.Gtype(t), _EP)    // MOVQ ${t}, EP
-    self.Emit("CMPQ", _AX, jit.Imm(a))      // CMPQ AX, ${a}
-    self.Sjmp("JL"  , _LB_range_error)      // JL   _range_error
-    self.Emit("CMPQ", _AX, jit.Imm(b))      // CMPQ AX, ${B}
-    self.Sjmp("JG"  , _LB_range_error)      // JG   _range_error
-}
-
-func (self *_Assembler) range_unsigned(i *rt.GoItab, t *rt.GoType, v uint64) {
-    self.Emit("MOVQ" , _VAR_st_Iv, _AX)         // MOVQ  st.Iv, AX
-    self.Emit("MOVQ" , jit.Gitab(i), _ET)       // MOVQ  ${i}, ET
-    self.Emit("MOVQ" , jit.Gtype(t), _EP)       // MOVQ  ${t}, EP
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ AX, AX
-    self.Sjmp("JS"   , _LB_range_error)         // JS    _range_error
-    self.Emit("CMPQ" , _AX, jit.Imm(int64(v)))  // CMPQ  AX, ${a}
-    self.Sjmp("JA"   , _LB_range_error)         // JA    _range_error
-}
-
-/** String Manipulating Routines **/
-
-var (
-    _F_unquote = jit.Imm(int64(native.S_unquote))
-)
-
-func (self *_Assembler) slice_from(p obj.Addr, d int64) {
-    self.Emit("MOVQ", p, _SI)   // MOVQ    ${p}, SI
-    self.slice_from_r(_SI, d)   // SLICE_R SI, ${d}
-}
-
-func (self *_Assembler) slice_from_r(p obj.Addr, d int64) {
-    self.Emit("LEAQ", jit.Sib(_IP, p, 1, 0), _DI)   // LEAQ (IP)(${p}), DI
-    self.Emit("NEGQ", p)                            // NEGQ ${p}
-    self.Emit("LEAQ", jit.Sib(_IC, p, 1, d), _SI)   // LEAQ d(IC)(${p}), SI
-}
-
-func (self *_Assembler) unquote_once(p obj.Addr, n obj.Addr, stack bool, copy bool) {
-    self.slice_from(_VAR_st_Iv, -1)                             // SLICE  st.Iv, $-1
-    self.Emit("CMPQ" , _VAR_st_Ep, jit.Imm(-1))                 // CMPQ   st.Ep, $-1
-    self.Sjmp("JE"   , "_noescape_{n}")                         // JE     _noescape_{n}
-    self.Byte(0x4c, 0x8d, 0x0d)                                 // LEAQ (PC), R9
-    self.Sref("_unquote_once_write_{n}", 4)
-    self.Sjmp("JMP" , "_escape_string")
-    self.Link("_noescape_{n}")                                  // _noescape_{n}:
-    if copy {
-        self.Emit("BTQ"  , jit.Imm(_F_copy_string), _ARG_fv)    
-        self.Sjmp("JNC", "_unquote_once_write_{n}")
-        self.Byte(0x4c, 0x8d, 0x0d)                             // LEAQ (PC), R9
-        self.Sref("_unquote_once_write_{n}", 4)
-        self.Sjmp("JMP", "_copy_string")
-    }
-    self.Link("_unquote_once_write_{n}")
-    self.Emit("MOVQ" , _SI, n)                                  // MOVQ   SI, ${n}
-    if stack {
-        self.Emit("MOVQ", _DI, p) 
-    } else {
-        self.WriteRecNotAX(10, _DI, p, false, false)
-    }
-}
-
-func (self *_Assembler) unquote_twice(p obj.Addr, n obj.Addr, stack bool) {
-    self.Emit("CMPQ" , _VAR_st_Ep, jit.Imm(-1))                     // CMPQ   st.Ep, $-1
-    self.Sjmp("JE"   , _LB_eof_error)                               // JE     _eof_error
-    self.Emit("CMPB" , jit.Sib(_IP, _IC, 1, -3), jit.Imm('\\'))     // CMPB   -3(IP)(IC), $'\\'
-    self.Sjmp("JNE"  , _LB_char_m3_error)                           // JNE    _char_m3_error
-    self.Emit("CMPB" , jit.Sib(_IP, _IC, 1, -2), jit.Imm('"'))      // CMPB   -2(IP)(IC), $'"'
-    self.Sjmp("JNE"  , _LB_char_m2_error)                           // JNE    _char_m2_error
-    self.slice_from(_VAR_st_Iv, -3)                                 // SLICE  st.Iv, $-3
-    self.Emit("MOVQ" , _SI, _AX)                                    // MOVQ   SI, AX
-    self.Emit("ADDQ" , _VAR_st_Iv, _AX)                             // ADDQ   st.Iv, AX
-    self.Emit("CMPQ" , _VAR_st_Ep, _AX)                             // CMPQ   st.Ep, AX
-    self.Sjmp("JE"   , "_noescape_{n}")                             // JE     _noescape_{n}
-    self.Byte(0x4c, 0x8d, 0x0d)                                     // LEAQ (PC), R9
-    self.Sref("_unquote_twice_write_{n}", 4)
-    self.Sjmp("JMP" , "_escape_string_twice")
-    self.Link("_noescape_{n}")                                      // _noescape_{n}:
-    self.Emit("BTQ"  , jit.Imm(_F_copy_string), _ARG_fv)    
-    self.Sjmp("JNC", "_unquote_twice_write_{n}") 
-    self.Byte(0x4c, 0x8d, 0x0d)                                     // LEAQ (PC), R9
-    self.Sref("_unquote_twice_write_{n}", 4)
-    self.Sjmp("JMP", "_copy_string")
-    self.Link("_unquote_twice_write_{n}")
-    self.Emit("MOVQ" , _SI, n)                                      // MOVQ   SI, ${n}
-    if stack {
-        self.Emit("MOVQ", _DI, p) 
-    } else {
-        self.WriteRecNotAX(12, _DI, p, false, false)
-    }
-}
-
-/** Memory Clearing Routines **/
-
-var (
-    _F_memclrHasPointers    = jit.Func(memclrHasPointers)
-    _F_memclrNoHeapPointers = jit.Func(memclrNoHeapPointers)
-)
-
-func (self *_Assembler) mem_clear_fn(ptrfree bool) {
-    if !ptrfree {
-        self.call_go(_F_memclrHasPointers)
-    } else {
-        self.call_go(_F_memclrNoHeapPointers)
-    }
-}
-
-func (self *_Assembler) mem_clear_rem(size int64, ptrfree bool) {
-    self.Emit("MOVQ", jit.Imm(size), _CX)               // MOVQ    ${size}, CX
-    self.Emit("MOVQ", jit.Ptr(_ST, 0), _AX)             // MOVQ    (ST), AX
-    self.Emit("MOVQ", jit.Sib(_ST, _AX, 1, 0), _AX)     // MOVQ    (ST)(AX), AX
-    self.Emit("SUBQ", _VP, _AX)                         // SUBQ    VP, AX
-    self.Emit("ADDQ", _AX, _CX)                         // ADDQ    AX, CX
-    self.Emit("MOVQ", _VP, jit.Ptr(_SP, 0))             // MOVQ    VP, (SP)
-    self.Emit("MOVQ", _CX, jit.Ptr(_SP, 8))             // MOVQ    CX, 8(SP)
-    self.mem_clear_fn(ptrfree)                          // CALL_GO memclr{Has,NoHeap}Pointers
-}
-
-/** Map Assigning Routines **/
-
-var (
-    _F_mapassign           = jit.Func(mapassign)
-    _F_mapassign_fast32    = jit.Func(mapassign_fast32)
-    _F_mapassign_faststr   = jit.Func(mapassign_faststr)
-    _F_mapassign_fast64ptr = jit.Func(mapassign_fast64ptr)
-)
-
-var (
-    _F_decodeJsonUnmarshaler obj.Addr
-    _F_decodeTextUnmarshaler obj.Addr
-)
-
-func init() {
-    _F_decodeJsonUnmarshaler = jit.Func(decodeJsonUnmarshaler)
-    _F_decodeTextUnmarshaler = jit.Func(decodeTextUnmarshaler)
-}
-
-func (self *_Assembler) mapaccess_ptr(t reflect.Type) {
-    if rt.MapType(rt.UnpackType(t)).IndirectElem() {
-        self.vfollow(t.Elem())
-    }
-}
-
-func (self *_Assembler) mapassign_std(t reflect.Type, v obj.Addr) {
-    self.Emit("LEAQ", v, _AX)               // LEAQ      ${v}, AX
-    self.mapassign_call(t, _F_mapassign)    // MAPASSIGN ${t}, mapassign
-}
-
-func (self *_Assembler) mapassign_str_fast(t reflect.Type, p obj.Addr, n obj.Addr) {
-    self.Emit("MOVQ", jit.Type(t), _AX)         // MOVQ    ${t}, AX
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0))     // MOVQ    AX, (SP)
-    self.Emit("MOVQ", _VP, jit.Ptr(_SP, 8))     // MOVQ    VP, 8(SP)
-    self.Emit("MOVQ", p, jit.Ptr(_SP, 16))      // MOVQ    ${p}, 16(SP)
-    self.Emit("MOVQ", n, jit.Ptr(_SP, 24))      // MOVQ    ${n}, 24(SP)
-    self.call_go(_F_mapassign_faststr)          // CALL_GO ${fn}
-    self.Emit("MOVQ", jit.Ptr(_SP, 32), _VP)    // MOVQ    32(SP), VP
-    self.mapaccess_ptr(t)
-}
-
-func (self *_Assembler) mapassign_call(t reflect.Type, fn obj.Addr) {
-    self.Emit("MOVQ", jit.Type(t), _SI)         // MOVQ    ${t}, SI
-    self.Emit("MOVQ", _SI, jit.Ptr(_SP, 0))     // MOVQ    SI, (SP)
-    self.Emit("MOVQ", _VP, jit.Ptr(_SP, 8))     // MOVQ    VP, 8(SP)
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 16))    // MOVQ    AX, 16(SP)
-    self.call_go(fn)                            // CALL_GO ${fn}
-    self.Emit("MOVQ", jit.Ptr(_SP, 24), _VP)    // MOVQ    24(SP), VP
-}
-
-func (self *_Assembler) mapassign_fastx(t reflect.Type, fn obj.Addr) {
-    self.mapassign_call(t, fn)
-    self.mapaccess_ptr(t)
-}
-
-func (self *_Assembler) mapassign_utext(t reflect.Type, addressable bool) {
-    pv := false
-    vk := t.Key()
-    tk := t.Key()
-
-    /* deref pointer if needed */
-    if vk.Kind() == reflect.Ptr {
-        pv = true
-        vk = vk.Elem()
-    }
-
-    /* addressable value with pointer receiver */
-    if addressable {
-        pv = false
-        tk = reflect.PtrTo(tk)
-    }
-
-    /* allocate the key, and call the unmarshaler */
-    self.valloc(vk, _DI)                        // VALLOC  ${vk}, DI
-    // must spill vk pointer since next call_go may invoke GC
-    self.Emit("MOVQ" , _DI, _VAR_vk)
-    self.Emit("MOVQ" , jit.Type(tk), _AX)       // MOVQ    ${tk}, AX
-    self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0))    // MOVQ    AX, (SP)
-    self.Emit("MOVQ" , _DI, jit.Ptr(_SP, 8))    // MOVQ    DI, 8(SP)
-    self.Emit("MOVOU", _VAR_sv, _X0)            // MOVOU   sv, X0
-    self.Emit("MOVOU", _X0, jit.Ptr(_SP, 16))   // MOVOU   X0, 16(SP)
-    self.call_go(_F_decodeTextUnmarshaler)      // CALL_GO decodeTextUnmarshaler
-    self.Emit("MOVQ" , jit.Ptr(_SP, 32), _ET)   // MOVQ    32(SP), ET
-    self.Emit("MOVQ" , jit.Ptr(_SP, 40), _EP)   // MOVQ    40(SP), EP
-    self.Emit("TESTQ", _ET, _ET)                // TESTQ   ET, ET
-    self.Sjmp("JNZ"  , _LB_error)               // JNZ     _error
-    self.Emit("MOVQ" , _VAR_vk, _AX)
-
-    /* select the correct assignment function */
-    if !pv {
-        self.mapassign_call(t, _F_mapassign)
-    } else {
-        self.mapassign_fastx(t, _F_mapassign_fast64ptr)
-    }
-}
-
-/** External Unmarshaler Routines **/
-
-var (
-    _F_skip_one = jit.Imm(int64(native.S_skip_one))
-    _F_skip_number = jit.Imm(int64(native.S_skip_number))
-)
-
-func (self *_Assembler) unmarshal_json(t reflect.Type, deref bool) {
-    self.call_sf(_F_skip_one)                                   // CALL_SF   skip_one
-    self.Emit("TESTQ", _AX, _AX)                                // TESTQ     AX, AX
-    self.Sjmp("JS"   , _LB_parsing_error_v)                     // JS        _parse_error_v
-    self.slice_from_r(_AX, 0)                                   // SLICE_R   AX, $0
-    self.Emit("MOVQ" , _DI, _VAR_sv_p)                          // MOVQ      DI, sv.p
-    self.Emit("MOVQ" , _SI, _VAR_sv_n)                          // MOVQ      SI, sv.n
-    self.unmarshal_func(t, _F_decodeJsonUnmarshaler, deref)     // UNMARSHAL json, ${t}, ${deref}
-}
-
-func (self *_Assembler) unmarshal_text(t reflect.Type, deref bool) {
-    self.parse_string()                                         // PARSE     STRING
-    self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true)        // UNQUOTE   once, sv.p, sv.n
-    self.unmarshal_func(t, _F_decodeTextUnmarshaler, deref)     // UNMARSHAL text, ${t}, ${deref}
-}
-
-func (self *_Assembler) unmarshal_func(t reflect.Type, fn obj.Addr, deref bool) {
-    pt := t
-    vk := t.Kind()
-
-    /* allocate the field if needed */
-    if deref && vk == reflect.Ptr {
-        self.Emit("MOVQ" , _VP, _AX)                // MOVQ   VP, AX
-        self.Emit("MOVQ" , jit.Ptr(_AX, 0), _AX)    // MOVQ   (AX), AX
-        self.Emit("TESTQ", _AX, _AX)                // TESTQ  AX, AX
-        self.Sjmp("JNZ"  , "_deref_{n}")            // JNZ    _deref_{n}
-        self.valloc(t.Elem(), _AX)                  // VALLOC ${t.Elem()}, AX
-        self.WritePtrAX(3, jit.Ptr(_VP, 0), false)    // MOVQ   AX, (VP)
-        self.Link("_deref_{n}")                     // _deref_{n}:
-    }
-
-    /* set value type */
-    self.Emit("MOVQ", jit.Type(pt), _CX)        // MOVQ ${pt}, CX
-    self.Emit("MOVQ", _CX, jit.Ptr(_SP, 0))     // MOVQ CX, (SP)
-
-    /* set value pointer */
-    if deref && vk == reflect.Ptr {
-        self.Emit("MOVQ", _AX, jit.Ptr(_SP, 8))     // MOVQ AX, 8(SP)
-    } else {
-        self.Emit("MOVQ", _VP, jit.Ptr(_SP, 8))     // MOVQ VP, 8(SP)
-    }
-
-    /* set the source string and call the unmarshaler */
-    self.Emit("MOVOU", _VAR_sv, _X0)            // MOVOU   sv, X0
-    self.Emit("MOVOU", _X0, jit.Ptr(_SP, 16))   // MOVOU   X0, 16(SP)
-    self.call_go(fn)                            // CALL_GO ${fn}
-    self.Emit("MOVQ" , jit.Ptr(_SP, 32), _ET)   // MOVQ    32(SP), ET
-    self.Emit("MOVQ" , jit.Ptr(_SP, 40), _EP)   // MOVQ    40(SP), EP
-    self.Emit("TESTQ", _ET, _ET)                // TESTQ   ET, ET
-    self.Sjmp("JNZ"  , _LB_error)               // JNZ     _error
-}
-
-/** Dynamic Decoding Routine **/
-
-var (
-    _F_decodeTypedPointer obj.Addr
-)
-
-func init() {
-    _F_decodeTypedPointer = jit.Func(decodeTypedPointer)
-}
-
-func (self *_Assembler) decode_dynamic(vt obj.Addr, vp obj.Addr) {
-    self.Emit("MOVQ" , _ARG_fv, _CX)            // MOVQ    fv, CX
-    self.Emit("MOVOU", _ARG_sp, _X0)            // MOVOU   sp, X0
-    self.Emit("MOVOU", _X0, jit.Ptr(_SP, 0))    // MOVOU   X0, (SP)
-    self.Emit("MOVQ" , _IC, jit.Ptr(_SP, 16))   // MOVQ    IC, 16(SP)
-    self.Emit("MOVQ" , vt, jit.Ptr(_SP, 24))    // MOVQ    ${vt}, 24(SP)
-    self.Emit("MOVQ" , vp, jit.Ptr(_SP, 32))    // MOVQ    ${vp}, 32(SP)
-    self.Emit("MOVQ" , _ST, jit.Ptr(_SP, 40))   // MOVQ    ST, 40(SP)
-    self.Emit("MOVQ" , _CX, jit.Ptr(_SP, 48))   // MOVQ    CX, 48(SP)
-    self.call_go(_F_decodeTypedPointer)         // CALL_GO decodeTypedPointer
-    self.Emit("MOVQ" , jit.Ptr(_SP, 64), _ET)   // MOVQ    64(SP), ET
-    self.Emit("MOVQ" , jit.Ptr(_SP, 72), _EP)   // MOVQ    72(SP), EP
-    self.Emit("MOVQ" , jit.Ptr(_SP, 56), _IC)   // MOVQ    56(SP), IC
-    self.Emit("TESTQ", _ET, _ET)                // TESTQ   ET, ET
-    self.Sjmp("JE", "_decode_dynamic_end_{n}")  // JE, _decode_dynamic_end_{n}
-    self.Emit("MOVQ", _I_json_MismatchTypeError, _AX) // MOVQ _I_json_MismatchTypeError, AX
-    self.Emit("CMPQ",  _ET, _AX)                // CMPQ ET, AX
-    self.Sjmp("JNE" , _LB_error)                // JNE  LB_error
-    self.Emit("MOVQ", _EP, _VAR_ic)             // MOVQ EP, VAR_ic
-    self.Emit("MOVQ", _ET, _VAR_et)             // MOVQ ET, VAR_et
-    self.Link("_decode_dynamic_end_{n}")
-    
-}
-
-/** OpCode Assembler Functions **/
-
-var (
-    _F_memequal         = jit.Func(memequal)
-    _F_memmove          = jit.Func(memmove)
-    _F_growslice        = jit.Func(growslice)
-    _F_makeslice        = jit.Func(makeslice)
-    _F_makemap_small    = jit.Func(makemap_small)
-    _F_mapassign_fast64 = jit.Func(mapassign_fast64)
-)
-
-var (
-    _F_lspace  = jit.Imm(int64(native.S_lspace))
-    _F_strhash = jit.Imm(int64(caching.S_strhash))
-)
-
-var (
-    _F_b64decode   = jit.Imm(int64(_subr__b64decode))
-    _F_decodeValue = jit.Imm(int64(_subr_decode_value))
-)
-
-var (
-    _F_skip_array  = jit.Imm(int64(native.S_skip_array))
-    _F_skip_object = jit.Imm(int64(native.S_skip_object))
-)
-
-var (
-    _F_FieldMap_GetCaseInsensitive obj.Addr
-    _Empty_Slice = make([]byte, 0)
-    _Zero_Base = int64(uintptr(((*rt.GoSlice)(unsafe.Pointer(&_Empty_Slice))).Ptr))
-)
-
-const (
-    _MODE_AVX2 = 1 << 2
-)
-
-const (
-    _Fe_ID   = int64(unsafe.Offsetof(caching.FieldEntry{}.ID))
-    _Fe_Name = int64(unsafe.Offsetof(caching.FieldEntry{}.Name))
-    _Fe_Hash = int64(unsafe.Offsetof(caching.FieldEntry{}.Hash))
-)
-
-const (
-    _Vk_Ptr       = int64(reflect.Ptr)
-    _Gt_KindFlags = int64(unsafe.Offsetof(rt.GoType{}.KindFlags))
-)
-
-func init() {
-    _F_FieldMap_GetCaseInsensitive = jit.Func((*caching.FieldMap).GetCaseInsensitive)
-}
-
-func (self *_Assembler) _asm_OP_any(_ *_Instr) {
-    self.Emit("MOVQ"   , jit.Ptr(_VP, 8), _CX)              // MOVQ    8(VP), CX
-    self.Emit("TESTQ"  , _CX, _CX)                          // TESTQ   CX, CX
-    self.Sjmp("JZ"     , "_decode_{n}")                     // JZ      _decode_{n}
-    self.Emit("CMPQ"   , _CX, _VP)                          // CMPQ    CX, VP
-    self.Sjmp("JE"     , "_decode_{n}")                     // JE      _decode_{n}
-    self.Emit("MOVQ"   , jit.Ptr(_VP, 0), _AX)              // MOVQ    (VP), AX
-    self.Emit("MOVBLZX", jit.Ptr(_AX, _Gt_KindFlags), _DX)  // MOVBLZX _Gt_KindFlags(AX), DX
-    self.Emit("ANDL"   , jit.Imm(rt.F_kind_mask), _DX)      // ANDL    ${F_kind_mask}, DX
-    self.Emit("CMPL"   , _DX, jit.Imm(_Vk_Ptr))             // CMPL    DX, ${reflect.Ptr}
-    self.Sjmp("JNE"    , "_decode_{n}")                     // JNE     _decode_{n}
-    self.Emit("LEAQ"   , jit.Ptr(_VP, 8), _DI)              // LEAQ    8(VP), DI
-    self.decode_dynamic(_AX, _DI)                           // DECODE  AX, DI
-    self.Sjmp("JMP"    , "_decode_end_{n}")                 // JMP     _decode_end_{n}
-    self.Link("_decode_{n}")                                // _decode_{n}:
-    self.Emit("MOVQ"   , _ARG_fv, _DF)                      // MOVQ    fv, DF
-    self.Emit("MOVQ"   , _ST, jit.Ptr(_SP, 0))              // MOVQ    _ST, (SP)
-    self.call(_F_decodeValue)                               // CALL    decodeValue
-    self.Emit("TESTQ"  , _EP, _EP)                          // TESTQ   EP, EP
-    self.Sjmp("JNZ"    , _LB_parsing_error)                 // JNZ     _parsing_error
-    self.Link("_decode_end_{n}")                            // _decode_end_{n}:
-}
-
-func (self *_Assembler) _asm_OP_dyn(p *_Instr) {
-    self.Emit("MOVQ"   , jit.Type(p.vt()), _ET)             // MOVQ    ${p.vt()}, ET
-    self.Emit("CMPQ"   , jit.Ptr(_VP, 8), jit.Imm(0))       // CMPQ    8(VP), $0
-    self.Sjmp("JE"     , _LB_type_error)                    // JE      _type_error
-    self.Emit("MOVQ"   , jit.Ptr(_VP, 0), _AX)              // MOVQ    (VP), AX
-    self.Emit("MOVQ"   , jit.Ptr(_AX, 8), _AX)              // MOVQ    8(AX), AX
-    self.Emit("MOVBLZX", jit.Ptr(_AX, _Gt_KindFlags), _DX)  // MOVBLZX _Gt_KindFlags(AX), DX
-    self.Emit("ANDL"   , jit.Imm(rt.F_kind_mask), _DX)      // ANDL    ${F_kind_mask}, DX
-    self.Emit("CMPL"   , _DX, jit.Imm(_Vk_Ptr))             // CMPL    DX, ${reflect.Ptr}
-    self.Sjmp("JNE"    , _LB_type_error)                    // JNE     _type_error
-    self.Emit("LEAQ"   , jit.Ptr(_VP, 8), _DI)              // LEAQ    8(VP), DI
-    self.decode_dynamic(_AX, _DI)                           // DECODE  AX, DI
-    self.Link("_decode_end_{n}")                            // _decode_end_{n}:
-}
-
-func (self *_Assembler) _asm_OP_str(_ *_Instr) {
-    self.parse_string()                                     // PARSE   STRING
-    self.unquote_once(jit.Ptr(_VP, 0), jit.Ptr(_VP, 8), false, true)     // UNQUOTE once, (VP), 8(VP)
-}
-
-func (self *_Assembler) _asm_OP_bin(_ *_Instr) {
-    self.parse_string()                                 // PARSE  STRING
-    self.slice_from(_VAR_st_Iv, -1)                     // SLICE  st.Iv, $-1
-    self.Emit("MOVQ" , _DI, jit.Ptr(_VP, 0))            // MOVQ   DI, (VP)
-    self.Emit("MOVQ" , _SI, jit.Ptr(_VP, 8))            // MOVQ   SI, 8(VP)
-    self.Emit("SHRQ" , jit.Imm(2), _SI)                 // SHRQ   $2, SI
-    self.Emit("LEAQ" , jit.Sib(_SI, _SI, 2, 0), _SI)    // LEAQ   (SI)(SI*2), SI
-    self.Emit("MOVQ" , _SI, jit.Ptr(_VP, 16))           // MOVQ   SI, 16(VP)
-    self.malloc(_SI, _SI)                               // MALLOC SI, SI
-
-    // TODO: due to base64x's bug, only use AVX mode now
-    self.Emit("MOVL", jit.Imm(_MODE_JSON), _CX)          //  MOVL $_MODE_JSON, CX
-
-    /* call the decoder */
-    self.Emit("XORL" , _DX, _DX)                // XORL  DX, DX
-    self.Emit("MOVQ" , _VP, _DI)                // MOVQ  VP, DI
-
-    self.Emit("MOVQ" , jit.Ptr(_VP, 0), _R9)    // MOVQ SI, (VP)
-    self.WriteRecNotAX(4, _SI, jit.Ptr(_VP, 0), true, false)    // XCHGQ SI, (VP) 
-    self.Emit("MOVQ" , _R9, _SI)
-
-    self.Emit("XCHGQ", _DX, jit.Ptr(_VP, 8))    // XCHGQ DX, 8(VP)
-    self.call(_F_b64decode)                     // CALL  b64decode
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ AX, AX
-    self.Sjmp("JS"   , _LB_base64_error)        // JS    _base64_error
-    self.Emit("MOVQ" , _AX, jit.Ptr(_VP, 8))    // MOVQ  AX, 8(VP)
-}
-
-func (self *_Assembler) _asm_OP_bool(_ *_Instr) {
-    self.Emit("LEAQ", jit.Ptr(_IC, 4), _AX)                     // LEAQ 4(IC), AX
-    self.Emit("CMPQ", _AX, _IL)                                 // CMPQ AX, IL
-    self.Sjmp("JA"  , _LB_eof_error)                            // JA   _eof_error
-    self.Emit("CMPB", jit.Sib(_IP, _IC, 1, 0), jit.Imm('f'))    // CMPB (IP)(IC), $'f'
-    self.Sjmp("JE"  , "_false_{n}")                             // JE   _false_{n}
-    self.Emit("MOVL", jit.Imm(_IM_true), _CX)                   // MOVL $"true", CX
-    self.Emit("CMPL", _CX, jit.Sib(_IP, _IC, 1, 0))             // CMPL CX, (IP)(IC)
-    self.Sjmp("JE" , "_bool_true_{n}")  
-
-    // try to skip the value
-    self.Emit("MOVQ", _IC, _VAR_ic)           
-    self.Emit("MOVQ", _T_bool, _ET)         
-    self.Emit("MOVQ", _ET, _VAR_et)
-    self.Byte(0x4c, 0x8d, 0x0d)         // LEAQ (PC), R9
-    self.Sref("_end_{n}", 4)
-    self.Emit("MOVQ", _R9, _VAR_pc)
-    self.Sjmp("JMP"  , _LB_skip_one) 
-
-    self.Link("_bool_true_{n}")
-    self.Emit("MOVQ", _AX, _IC)                                 // MOVQ AX, IC
-    self.Emit("MOVB", jit.Imm(1), jit.Ptr(_VP, 0))              // MOVB $1, (VP)
-    self.Sjmp("JMP" , "_end_{n}")                               // JMP  _end_{n}
-    self.Link("_false_{n}")                                     // _false_{n}:
-    self.Emit("ADDQ", jit.Imm(1), _AX)                          // ADDQ $1, AX
-    self.Emit("ADDQ", jit.Imm(1), _IC)                          // ADDQ $1, IC
-    self.Emit("CMPQ", _AX, _IL)                                 // CMPQ AX, IL
-    self.Sjmp("JA"  , _LB_eof_error)                            // JA   _eof_error
-    self.Emit("MOVL", jit.Imm(_IM_alse), _CX)                   // MOVL $"alse", CX
-    self.Emit("CMPL", _CX, jit.Sib(_IP, _IC, 1, 0))             // CMPL CX, (IP)(IC)
-    self.Sjmp("JNE" , _LB_im_error)                             // JNE  _im_error
-    self.Emit("MOVQ", _AX, _IC)                                 // MOVQ AX, IC
-    self.Emit("XORL", _AX, _AX)                                 // XORL AX, AX
-    self.Emit("MOVB", _AX, jit.Ptr(_VP, 0))                     // MOVB AX, (VP)
-    self.Link("_end_{n}")                                       // _end_{n}:
-}
-
-func (self *_Assembler) _asm_OP_num(_ *_Instr) {
-    self.Emit("MOVQ", jit.Imm(0), _VAR_fl)
-    self.Emit("CMPB", jit.Sib(_IP, _IC, 1, 0), jit.Imm('"'))
-    self.Emit("MOVQ", _IC, _BP)
-    self.Sjmp("JNE", "_skip_number_{n}")
-    self.Emit("MOVQ", jit.Imm(1), _VAR_fl)
-    self.Emit("ADDQ", jit.Imm(1), _IC)
-    self.Link("_skip_number_{n}")
-
-    /* call skip_number */
-    self.call_sf(_F_skip_number)                   // CALL_SF skip_one
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ   AX, AX
-    self.Sjmp("JNS"   , "_num_next_{n}")
-
-    /* call skip one */
-    self.Emit("MOVQ", _BP, _VAR_ic)           
-    self.Emit("MOVQ", _T_number, _ET)       
-    self.Emit("MOVQ", _ET, _VAR_et)
-    self.Byte(0x4c, 0x8d, 0x0d)       
-    self.Sref("_num_end_{n}", 4)
-    self.Emit("MOVQ", _R9, _VAR_pc)
-    self.Sjmp("JMP"  , _LB_skip_one)
-
-    /* assgin string */
-    self.Link("_num_next_{n}")
-    self.slice_from_r(_AX, 0)
-    self.Emit("BTQ", jit.Imm(_F_copy_string), _ARG_fv)
-    self.Sjmp("JNC", "_num_write_{n}")
-    self.Byte(0x4c, 0x8d, 0x0d)                 // LEAQ (PC), R9
-    self.Sref("_num_write_{n}", 4)
-    self.Sjmp("JMP", "_copy_string")
-    self.Link("_num_write_{n}")
-    self.Emit("MOVQ", _SI, jit.Ptr(_VP, 8))     // MOVQ  SI, 8(VP)
-    self.WriteRecNotAX(13, _DI, jit.Ptr(_VP, 0), false, false)   
-    
-    /* check if quoted */
-    self.Emit("CMPQ", _VAR_fl, jit.Imm(1))
-    self.Sjmp("JNE", "_num_end_{n}")
-    self.Emit("CMPB", jit.Sib(_IP, _IC, 1, 0), jit.Imm('"'))
-    self.Sjmp("JNE", _LB_char_0_error)
-    self.Emit("ADDQ", jit.Imm(1), _IC)
-    self.Link("_num_end_{n}")
-}
-
-func (self *_Assembler) _asm_OP_i8(ins *_Instr) {
-    var pin = "_i8_end_{n}"
-    self.parse_signed(int8Type, pin, -1)                                                 // PARSE int8
-    self.range_signed(_I_int8, _T_int8, math.MinInt8, math.MaxInt8)     // RANGE int8
-    self.Emit("MOVB", _AX, jit.Ptr(_VP, 0))                             // MOVB  AX, (VP)
-    self.Link(pin)
-}
-
-func (self *_Assembler) _asm_OP_i16(ins *_Instr) {
-    var pin = "_i16_end_{n}"
-    self.parse_signed(int16Type, pin, -1)                                                     // PARSE int16
-    self.range_signed(_I_int16, _T_int16, math.MinInt16, math.MaxInt16)     // RANGE int16
-    self.Emit("MOVW", _AX, jit.Ptr(_VP, 0))                                 // MOVW  AX, (VP)
-    self.Link(pin)
-}
-
-func (self *_Assembler) _asm_OP_i32(ins *_Instr) {
-    var pin = "_i32_end_{n}"
-    self.parse_signed(int32Type, pin, -1)                                                     // PARSE int32
-    self.range_signed(_I_int32, _T_int32, math.MinInt32, math.MaxInt32)     // RANGE int32
-    self.Emit("MOVL", _AX, jit.Ptr(_VP, 0))                                 // MOVL  AX, (VP)
-    self.Link(pin)
-}
-
-func (self *_Assembler) _asm_OP_i64(ins *_Instr) {
-    var pin = "_i64_end_{n}"
-    self.parse_signed(int64Type, pin, -1)                         // PARSE int64
-    self.Emit("MOVQ", _VAR_st_Iv, _AX)          // MOVQ  st.Iv, AX
-    self.Emit("MOVQ", _AX, jit.Ptr(_VP, 0))     // MOVQ  AX, (VP)
-    self.Link(pin)
-}
-
-func (self *_Assembler) _asm_OP_u8(ins *_Instr) {
-    var pin = "_u8_end_{n}"
-    self.parse_unsigned(uint8Type, pin, -1)                                   // PARSE uint8
-    self.range_unsigned(_I_uint8, _T_uint8, math.MaxUint8)  // RANGE uint8
-    self.Emit("MOVB", _AX, jit.Ptr(_VP, 0))                 // MOVB  AX, (VP)
-    self.Link(pin)
-}
-
-func (self *_Assembler) _asm_OP_u16(ins *_Instr) {
-    var pin = "_u16_end_{n}"
-    self.parse_unsigned(uint16Type, pin, -1)                                       // PARSE uint16
-    self.range_unsigned(_I_uint16, _T_uint16, math.MaxUint16)   // RANGE uint16
-    self.Emit("MOVW", _AX, jit.Ptr(_VP, 0))                     // MOVW  AX, (VP)
-    self.Link(pin)
-}
-
-func (self *_Assembler) _asm_OP_u32(ins *_Instr) {
-    var pin = "_u32_end_{n}"
-    self.parse_unsigned(uint32Type, pin, -1)                                       // PARSE uint32
-    self.range_unsigned(_I_uint32, _T_uint32, math.MaxUint32)   // RANGE uint32
-    self.Emit("MOVL", _AX, jit.Ptr(_VP, 0))                     // MOVL  AX, (VP)
-    self.Link(pin)
-}
-
-func (self *_Assembler) _asm_OP_u64(ins *_Instr) {
-    var pin = "_u64_end_{n}"
-    self.parse_unsigned(uint64Type, pin, -1)                       // PARSE uint64
-    self.Emit("MOVQ", _VAR_st_Iv, _AX)          // MOVQ  st.Iv, AX
-    self.Emit("MOVQ", _AX, jit.Ptr(_VP, 0))     // MOVQ  AX, (VP)
-    self.Link(pin)
-}
-
-func (self *_Assembler) _asm_OP_f32(ins *_Instr) {
-    var pin = "_f32_end_{n}"
-    self.parse_number(float32Type, pin, -1)                         // PARSE NUMBER
-    self.range_single()                         // RANGE float32
-    self.Emit("MOVSS", _X0, jit.Ptr(_VP, 0))    // MOVSS X0, (VP)
-    self.Link(pin)
-}
-
-func (self *_Assembler) _asm_OP_f64(ins *_Instr) {
-    var pin = "_f64_end_{n}"
-    self.parse_number(float64Type, pin, -1)                         // PARSE NUMBER
-    self.Emit("MOVSD", _VAR_st_Dv, _X0)         // MOVSD st.Dv, X0
-    self.Emit("MOVSD", _X0, jit.Ptr(_VP, 0))    // MOVSD X0, (VP)
-    self.Link(pin)
-}
-
-func (self *_Assembler) _asm_OP_unquote(ins *_Instr) {
-    self.check_eof(2)
-    self.Emit("CMPB", jit.Sib(_IP, _IC, 1, 0), jit.Imm('\\'))   // CMPB    (IP)(IC), $'\\'
-    self.Sjmp("JNE" , _LB_char_0_error)                         // JNE     _char_0_error
-    self.Emit("CMPB", jit.Sib(_IP, _IC, 1, 1), jit.Imm('"'))    // CMPB    1(IP)(IC), $'"'
-    self.Sjmp("JNE" , _LB_char_1_error)                         // JNE     _char_1_error
-    self.Emit("ADDQ", jit.Imm(2), _IC)                          // ADDQ    $2, IC
-    self.parse_string()                                         // PARSE   STRING
-    self.unquote_twice(jit.Ptr(_VP, 0), jit.Ptr(_VP, 8), false)        // UNQUOTE twice, (VP), 8(VP)
-}
-
-func (self *_Assembler) _asm_OP_nil_1(_ *_Instr) {
-    self.Emit("XORL", _AX, _AX)                 // XORL AX, AX
-    self.Emit("MOVQ", _AX, jit.Ptr(_VP, 0))     // MOVQ AX, (VP)
-}
-
-func (self *_Assembler) _asm_OP_nil_2(_ *_Instr) {
-    self.Emit("PXOR" , _X0, _X0)                // PXOR  X0, X0
-    self.Emit("MOVOU", _X0, jit.Ptr(_VP, 0))    // MOVOU X0, (VP)
-}
-
-func (self *_Assembler) _asm_OP_nil_3(_ *_Instr) {
-    self.Emit("XORL" , _AX, _AX)                // XORL  AX, AX
-    self.Emit("PXOR" , _X0, _X0)                // PXOR  X0, X0
-    self.Emit("MOVOU", _X0, jit.Ptr(_VP, 0))    // MOVOU X0, (VP)
-    self.Emit("MOVQ" , _AX, jit.Ptr(_VP, 16))   // MOVOU X0, 16(VP)
-}
-
-func (self *_Assembler) _asm_OP_deref(p *_Instr) {
-    self.vfollow(p.vt())
-}
-
-func (self *_Assembler) _asm_OP_index(p *_Instr) {
-    self.Emit("MOVQ", jit.Imm(p.i64()), _AX)    // MOVQ ${p.vi()}, AX
-    self.Emit("ADDQ", _AX, _VP)                 // ADDQ _AX, _VP
-}
-
-func (self *_Assembler) _asm_OP_is_null(p *_Instr) {
-    self.Emit("LEAQ"   , jit.Ptr(_IC, 4), _AX)                          // LEAQ    4(IC), AX
-    self.Emit("CMPQ"   , _AX, _IL)                                      // CMPQ    AX, IL
-    self.Sjmp("JA"     , "_not_null_{n}")                               // JA      _not_null_{n}
-    self.Emit("CMPL"   , jit.Sib(_IP, _IC, 1, 0), jit.Imm(_IM_null))    // CMPL    (IP)(IC), $"null"
-    self.Emit("CMOVQEQ", _AX, _IC)                                      // CMOVQEQ AX, IC
-    self.Xjmp("JE"     , p.vi())                                        // JE      {p.vi()}
-    self.Link("_not_null_{n}")                                          // _not_null_{n}:
-}
-
-func (self *_Assembler) _asm_OP_is_null_quote(p *_Instr) {
-    self.Emit("LEAQ"   , jit.Ptr(_IC, 5), _AX)                          // LEAQ    4(IC), AX
-    self.Emit("CMPQ"   , _AX, _IL)                                      // CMPQ    AX, IL
-    self.Sjmp("JA"     , "_not_null_quote_{n}")                         // JA      _not_null_quote_{n}
-    self.Emit("CMPL"   , jit.Sib(_IP, _IC, 1, 0), jit.Imm(_IM_null))    // CMPL    (IP)(IC), $"null"
-    self.Sjmp("JNE"    , "_not_null_quote_{n}")                         // JNE     _not_null_quote_{n}
-    self.Emit("CMPB"   , jit.Sib(_IP, _IC, 1, 4), jit.Imm('"'))         // CMPB    4(IP)(IC), $'"'
-    self.Emit("CMOVQEQ", _AX, _IC)                                      // CMOVQEQ AX, IC
-    self.Xjmp("JE"     , p.vi())                                        // JE      {p.vi()}
-    self.Link("_not_null_quote_{n}")                                    // _not_null_quote_{n}:
-}
-
-func (self *_Assembler) _asm_OP_map_init(_ *_Instr) {
-    self.Emit("MOVQ" , jit.Ptr(_VP, 0), _AX)    // MOVQ    (VP), AX
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ   AX, AX
-    self.Sjmp("JNZ"  , "_end_{n}")              // JNZ     _end_{n}
-    self.call_go(_F_makemap_small)              // CALL_GO makemap_small
-    self.Emit("MOVQ" , jit.Ptr(_SP, 0), _AX)    // MOVQ    (SP), AX
-    self.WritePtrAX(6, jit.Ptr(_VP, 0), false)    // MOVQ    AX, (VP)
-    self.Link("_end_{n}")                       // _end_{n}:
-    self.Emit("MOVQ" , _AX, _VP)                // MOVQ    AX, VP
-}
-
-func (self *_Assembler) _asm_OP_map_key_i8(p *_Instr) {
-    self.parse_signed(int8Type, "", p.vi())                                                 // PARSE     int8
-    self.range_signed(_I_int8, _T_int8, math.MinInt8, math.MaxInt8)     // RANGE     int8
-    self.match_char('"')
-    self.mapassign_std(p.vt(), _VAR_st_Iv)                              // MAPASSIGN int8, mapassign, st.Iv
-}
-
-func (self *_Assembler) _asm_OP_map_key_i16(p *_Instr) {
-    self.parse_signed(int16Type, "", p.vi())                                                     // PARSE     int16
-    self.range_signed(_I_int16, _T_int16, math.MinInt16, math.MaxInt16)     // RANGE     int16
-    self.match_char('"')
-    self.mapassign_std(p.vt(), _VAR_st_Iv)                                  // MAPASSIGN int16, mapassign, st.Iv
-}
-
-func (self *_Assembler) _asm_OP_map_key_i32(p *_Instr) {
-    self.parse_signed(int32Type, "", p.vi())                                                     // PARSE     int32
-    self.range_signed(_I_int32, _T_int32, math.MinInt32, math.MaxInt32)     // RANGE     int32
-    self.match_char('"')
-    if vt := p.vt(); !mapfast(vt) {
-        self.mapassign_std(vt, _VAR_st_Iv)                                  // MAPASSIGN int32, mapassign, st.Iv
-    } else {
-        self.mapassign_fastx(vt, _F_mapassign_fast32)                       // MAPASSIGN int32, mapassign_fast32
-    }
-}
-
-func (self *_Assembler) _asm_OP_map_key_i64(p *_Instr) {
-    self.parse_signed(int64Type, "", p.vi())                                 // PARSE     int64
-    self.match_char('"')
-    if vt := p.vt(); !mapfast(vt) {
-        self.mapassign_std(vt, _VAR_st_Iv)              // MAPASSIGN int64, mapassign, st.Iv
-    } else {
-        self.Emit("MOVQ", _VAR_st_Iv, _AX)              // MOVQ      st.Iv, AX
-        self.mapassign_fastx(vt, _F_mapassign_fast64)   // MAPASSIGN int64, mapassign_fast64
-    }
-}
-
-func (self *_Assembler) _asm_OP_map_key_u8(p *_Instr) {
-    self.parse_unsigned(uint8Type, "", p.vi())                                   // PARSE     uint8
-    self.range_unsigned(_I_uint8, _T_uint8, math.MaxUint8)  // RANGE     uint8
-    self.match_char('"')
-    self.mapassign_std(p.vt(), _VAR_st_Iv)                  // MAPASSIGN uint8, vt.Iv
-}
-
-func (self *_Assembler) _asm_OP_map_key_u16(p *_Instr) {
-    self.parse_unsigned(uint16Type, "", p.vi())                                       // PARSE     uint16
-    self.range_unsigned(_I_uint16, _T_uint16, math.MaxUint16)   // RANGE     uint16
-    self.match_char('"')
-    self.mapassign_std(p.vt(), _VAR_st_Iv)                      // MAPASSIGN uint16, vt.Iv
-}
-
-func (self *_Assembler) _asm_OP_map_key_u32(p *_Instr) {
-    self.parse_unsigned(uint32Type, "", p.vi())                                       // PARSE     uint32
-    self.range_unsigned(_I_uint32, _T_uint32, math.MaxUint32)   // RANGE     uint32
-    self.match_char('"')
-    if vt := p.vt(); !mapfast(vt) {
-        self.mapassign_std(vt, _VAR_st_Iv)                      // MAPASSIGN uint32, vt.Iv
-    } else {
-        self.mapassign_fastx(vt, _F_mapassign_fast32)           // MAPASSIGN uint32, mapassign_fast32
-    }
-}
-
-func (self *_Assembler) _asm_OP_map_key_u64(p *_Instr) {
-    self.parse_unsigned(uint64Type, "", p.vi())                                       // PARSE     uint64
-    self.match_char('"')
-    if vt := p.vt(); !mapfast(vt) {
-        self.mapassign_std(vt, _VAR_st_Iv)                      // MAPASSIGN uint64, vt.Iv
-    } else {
-        self.Emit("MOVQ", _VAR_st_Iv, _AX)                      // MOVQ      st.Iv, AX
-        self.mapassign_fastx(vt, _F_mapassign_fast64)           // MAPASSIGN uint64, mapassign_fast64
-    }
-}
-
-func (self *_Assembler) _asm_OP_map_key_f32(p *_Instr) {
-    self.parse_number(float32Type, "", p.vi())                     // PARSE     NUMBER
-    self.range_single()                     // RANGE     float32
-    self.Emit("MOVSS", _X0, _VAR_st_Dv)     // MOVSS     X0, st.Dv
-    self.match_char('"')
-    self.mapassign_std(p.vt(), _VAR_st_Dv)  // MAPASSIGN ${p.vt()}, mapassign, st.Dv
-}
-
-func (self *_Assembler) _asm_OP_map_key_f64(p *_Instr) {
-    self.parse_number(float64Type, "", p.vi())                     // PARSE     NUMBER
-    self.match_char('"')
-    self.mapassign_std(p.vt(), _VAR_st_Dv)  // MAPASSIGN ${p.vt()}, mapassign, st.Dv
-}
-
-func (self *_Assembler) _asm_OP_map_key_str(p *_Instr) {
-    self.parse_string()                          // PARSE     STRING
-    self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true)      // UNQUOTE   once, sv.p, sv.n
-    if vt := p.vt(); !mapfast(vt) {
-        self.valloc(vt.Key(), _DI)
-        self.Emit("MOVOU", _VAR_sv, _X0)
-        self.Emit("MOVOU", _X0, jit.Ptr(_DI, 0))
-        self.mapassign_std(vt, jit.Ptr(_DI, 0))        
-    } else {
-        self.Emit("MOVQ", _VAR_sv_p, _DI)        // MOVQ      sv.p, DI
-        self.Emit("MOVQ", _VAR_sv_n, _SI)        // MOVQ      sv.n, SI
-        self.mapassign_str_fast(vt, _DI, _SI)    // MAPASSIGN string, DI, SI
-    }
-}
-
-func (self *_Assembler) _asm_OP_map_key_utext(p *_Instr) {
-    self.parse_string()                         // PARSE     STRING
-    self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true)     // UNQUOTE   once, sv.p, sv.n
-    self.mapassign_utext(p.vt(), false)         // MAPASSIGN utext, ${p.vt()}, false
-}
-
-func (self *_Assembler) _asm_OP_map_key_utext_p(p *_Instr) {
-    self.parse_string()                         // PARSE     STRING
-    self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, false)     // UNQUOTE   once, sv.p, sv.n
-    self.mapassign_utext(p.vt(), true)          // MAPASSIGN utext, ${p.vt()}, true
-}
-
-func (self *_Assembler) _asm_OP_array_skip(_ *_Instr) {
-    self.call_sf(_F_skip_array)                 // CALL_SF skip_array
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ   AX, AX
-    self.Sjmp("JS"   , _LB_parsing_error_v)     // JS      _parse_error_v
-}
-
-func (self *_Assembler) _asm_OP_array_clear(p *_Instr) {
-    self.mem_clear_rem(p.i64(), true)
-}
-
-func (self *_Assembler) _asm_OP_array_clear_p(p *_Instr) {
-    self.mem_clear_rem(p.i64(), false)
-}
-
-func (self *_Assembler) _asm_OP_slice_init(p *_Instr) {
-    self.Emit("XORL" , _AX, _AX)                    // XORL    AX, AX
-    self.Emit("MOVQ" , _AX, jit.Ptr(_VP, 8))        // MOVQ    AX, 8(VP)
-    self.Emit("MOVQ" , jit.Ptr(_VP, 16), _AX)       // MOVQ    16(VP), AX
-    self.Emit("TESTQ", _AX, _AX)                    // TESTQ   AX, AX
-    self.Sjmp("JNZ"  , "_done_{n}")                 // JNZ     _done_{n}
-    self.Emit("MOVQ" , jit.Imm(_MinSlice), _CX)     // MOVQ    ${_MinSlice}, CX
-    self.Emit("MOVQ" , _CX, jit.Ptr(_VP, 16))       // MOVQ    CX, 16(VP)
-    self.Emit("MOVQ" , jit.Type(p.vt()), _DX)       // MOVQ    ${p.vt()}, DX
-    self.Emit("MOVQ" , _DX, jit.Ptr(_SP, 0))        // MOVQ    DX, (SP)
-    self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 8))        // MOVQ    AX, 8(SP)
-    self.Emit("MOVQ" , _CX, jit.Ptr(_SP, 16))       // MOVQ    CX, 16(SP)
-    self.call_go(_F_makeslice)                      // CALL_GO makeslice
-    self.Emit("MOVQ" , jit.Ptr(_SP, 24), _AX)       // MOVQ    24(SP), AX
-    self.WritePtrAX(7, jit.Ptr(_VP, 0), false)      // MOVQ    AX, (VP)
-    self.Link("_done_{n}")                          // _done_{n}:
-    self.Emit("XORL" , _AX, _AX)                    // XORL    AX, AX
-    self.Emit("MOVQ" , _AX, jit.Ptr(_VP, 8))        // MOVQ    AX, 8(VP)
-}
-
-func (self *_Assembler) _asm_OP_check_empty(p *_Instr) {
-    rbracket := p.vb()
-    if rbracket == ']' {
-        self.check_eof(1)
-        self.Emit("LEAQ", jit.Ptr(_IC, 1), _AX)                              // LEAQ    1(IC), AX
-        self.Emit("CMPB", jit.Sib(_IP, _IC, 1, 0), jit.Imm(int64(rbracket))) // CMPB    (IP)(IC), ']'
-        self.Sjmp("JNE" , "_not_empty_array_{n}")                            // JNE     _not_empty_array_{n}
-        self.Emit("MOVQ", _AX, _IC)                                          // MOVQ    AX, IC
-        self.Emit("MOVQ", jit.Imm(_Zero_Base), _AX)
-        self.WritePtrAX(9, jit.Ptr(_VP, 0), false)
-        self.Emit("PXOR" , _X0, _X0)                                         // PXOR    X0, X0
-        self.Emit("MOVOU", _X0, jit.Ptr(_VP, 8))                             // MOVOU   X0, 8(VP)
-        self.Xjmp("JMP" , p.vi())                                            // JMP     {p.vi()}
-        self.Link("_not_empty_array_{n}")
-    } else {
-        panic("only implement check empty array here!")
-    }
-}
-
-func (self *_Assembler) _asm_OP_slice_append(p *_Instr) {
-    self.Emit("MOVQ" , jit.Ptr(_VP, 8), _AX)            // MOVQ    8(VP), AX
-    self.Emit("CMPQ" , _AX, jit.Ptr(_VP, 16))           // CMPQ    AX, 16(VP)
-    self.Sjmp("JB"   , "_index_{n}")                    // JB      _index_{n}
-    self.Emit("MOVQ" , jit.Type(p.vt()), _AX)           // MOVQ    ${p.vt()}, AX
-    self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0))            // MOVQ    AX, (SP)
-    self.Emit("MOVOU", jit.Ptr(_VP, 0), _X0)            // MOVOU   (VP), X0
-    self.Emit("MOVOU", _X0, jit.Ptr(_SP, 8))            // MOVOU   X0, 8(SP)
-    self.Emit("MOVQ" , jit.Ptr(_VP, 16), _AX)           // MOVQ    16(VP), AX
-    self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 24))           // MOVQ    AX, 24(SP)
-    self.Emit("SHLQ" , jit.Imm(1), _AX)                 // SHLQ    $1, AX
-    self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 32))           // MOVQ    AX, 32(SP)
-    self.call_go(_F_growslice)                          // CALL_GO growslice
-    self.Emit("MOVQ" , jit.Ptr(_SP, 40), _DI)           // MOVQ    40(SP), DI
-    self.Emit("MOVQ" , jit.Ptr(_SP, 48), _AX)           // MOVQ    48(SP), AX
-    self.Emit("MOVQ" , jit.Ptr(_SP, 56), _SI)           // MOVQ    56(SP), SI
-    self.WriteRecNotAX(8, _DI, jit.Ptr(_VP, 0), true, true)// MOVQ    DI, (VP)
-    self.Emit("MOVQ" , _AX, jit.Ptr(_VP, 8))            // MOVQ    AX, 8(VP)
-    self.Emit("MOVQ" , _SI, jit.Ptr(_VP, 16))           // MOVQ    SI, 16(VP)
-
-    // because growslice not zero memory {oldcap, newlen} when append et not has ptrdata.
-    // but we should zero it, avoid decode it as random values.
-    if rt.UnpackType(p.vt()).PtrData == 0 {
-        self.Emit("SUBQ" , _AX, _SI)                        // MOVQ    AX, SI
-    
-        self.Emit("ADDQ" , jit.Imm(1), jit.Ptr(_VP, 8))     // ADDQ    $1, 8(VP)
-        self.Emit("MOVQ" , _DI, _VP)                        // MOVQ    DI, VP
-        self.Emit("MOVQ" , jit.Imm(int64(p.vlen())), _CX)   // MOVQ    ${p.vlen()}, CX
-        self.From("MULQ" , _CX)                             // MULQ    CX
-        self.Emit("ADDQ" , _AX, _VP)                        // ADDQ    AX, VP
-
-        self.Emit("MOVQ" , _SI, _AX)                        // MOVQ    SI, AX
-        self.From("MULQ" , _CX)                             // MULQ    CX
-        self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 8))            // MOVQ    AX, 8(SP)
-
-        self.Emit("MOVQ" , _VP, jit.Ptr(_SP, 0))            // MOVQ    VP, (SP)
-        self.mem_clear_fn(true)                             // CALL_GO memclr{Has,NoHeap}
-        self.Sjmp("JMP", "_append_slice_end_{n}")           // JMP    _append_slice_end_{n}
-    }
-
-    self.Link("_index_{n}")                             // _index_{n}:
-    self.Emit("ADDQ" , jit.Imm(1), jit.Ptr(_VP, 8))     // ADDQ    $1, 8(VP)
-    self.Emit("MOVQ" , jit.Ptr(_VP, 0), _VP)            // MOVQ    (VP), VP
-    self.Emit("MOVQ" , jit.Imm(int64(p.vlen())), _CX)   // MOVQ    ${p.vlen()}, CX
-    self.From("MULQ" , _CX)                             // MULQ    CX
-    self.Emit("ADDQ" , _AX, _VP)                        // ADDQ    AX, VP
-    self.Link("_append_slice_end_{n}")
-}
-
-func (self *_Assembler) _asm_OP_object_skip(_ *_Instr) {
-    self.call_sf(_F_skip_object)                // CALL_SF skip_object
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ   AX, AX
-    self.Sjmp("JS"   , _LB_parsing_error_v)     // JS      _parse_error_v
-}
-
-func (self *_Assembler) _asm_OP_object_next(_ *_Instr) {
-    self.call_sf(_F_skip_one)                   // CALL_SF skip_one
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ   AX, AX
-    self.Sjmp("JS"   , _LB_parsing_error_v)     // JS      _parse_error_v
-}
-
-func (self *_Assembler) _asm_OP_struct_field(p *_Instr) {
-    assert_eq(caching.FieldEntrySize, 32, "invalid field entry size")
-    self.Emit("MOVQ" , jit.Imm(-1), _AX)                        // MOVQ    $-1, AX
-    self.Emit("MOVQ" , _AX, _VAR_sr)                            // MOVQ    AX, sr
-    self.parse_string()                                         // PARSE   STRING
-    self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, false)                     // UNQUOTE once, sv.p, sv.n
-    self.Emit("LEAQ" , _VAR_sv, _AX)                            // LEAQ    sv, AX
-    self.Emit("XORL" , _CX, _CX)                                // XORL    CX, CX
-    self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0))                    // MOVQ    AX, (SP)
-    self.Emit("MOVQ" , _CX, jit.Ptr(_SP, 8))                    // MOVQ    CX, 8(SP)
-    self.call_go(_F_strhash)                                    // CALL_GO strhash
-    self.Emit("MOVQ" , jit.Ptr(_SP, 16), _AX)                   // MOVQ    16(SP), AX
-    self.Emit("MOVQ" , _AX, _R9)                                // MOVQ    AX, R9
-    self.Emit("MOVQ" , jit.Imm(freezeFields(p.vf())), _CX)      // MOVQ    ${p.vf()}, CX
-    self.Emit("MOVQ" , jit.Ptr(_CX, caching.FieldMap_b), _SI)   // MOVQ    FieldMap.b(CX), SI
-    self.Emit("MOVQ" , jit.Ptr(_CX, caching.FieldMap_N), _CX)   // MOVQ    FieldMap.N(CX), CX
-    self.Emit("TESTQ", _CX, _CX)                                // TESTQ   CX, CX
-    self.Sjmp("JZ"   , "_try_lowercase_{n}")                    // JZ      _try_lowercase_{n}
-    self.Link("_loop_{n}")                                      // _loop_{n}:
-    self.Emit("XORL" , _DX, _DX)                                // XORL    DX, DX
-    self.From("DIVQ" , _CX)                                     // DIVQ    CX
-    self.Emit("LEAQ" , jit.Ptr(_DX, 1), _AX)                    // LEAQ    1(DX), AX
-    self.Emit("SHLQ" , jit.Imm(5), _DX)                         // SHLQ    $5, DX
-    self.Emit("LEAQ" , jit.Sib(_SI, _DX, 1, 0), _DI)            // LEAQ    (SI)(DX), DI
-    self.Emit("MOVQ" , jit.Ptr(_DI, _Fe_Hash), _R8)             // MOVQ    FieldEntry.Hash(DI), R8
-    self.Emit("TESTQ", _R8, _R8)                                // TESTQ   R8, R8
-    self.Sjmp("JZ"   , "_try_lowercase_{n}")                    // JZ      _try_lowercase_{n}
-    self.Emit("CMPQ" , _R8, _R9)                                // CMPQ    R8, R9
-    self.Sjmp("JNE"  , "_loop_{n}")                             // JNE     _loop_{n}
-    self.Emit("MOVQ" , jit.Ptr(_DI, _Fe_Name + 8), _DX)         // MOVQ    FieldEntry.Name+8(DI), DX
-    self.Emit("CMPQ" , _DX, _VAR_sv_n)                          // CMPQ    DX, sv.n
-    self.Sjmp("JNE"  , "_loop_{n}")                             // JNE     _loop_{n}
-    self.Emit("MOVQ" , jit.Ptr(_DI, _Fe_ID), _R8)               // MOVQ    FieldEntry.ID(DI), R8
-    self.Emit("MOVQ" , _AX, _VAR_ss_AX)                         // MOVQ    AX, ss.AX
-    self.Emit("MOVQ" , _CX, _VAR_ss_CX)                         // MOVQ    CX, ss.CX
-    self.Emit("MOVQ" , _SI, _VAR_ss_SI)                         // MOVQ    SI, ss.SI
-    self.Emit("MOVQ" , _R8, _VAR_ss_R8)                         // MOVQ    R8, ss.R8
-    self.Emit("MOVQ" , _R9, _VAR_ss_R9)                         // MOVQ    R9, ss.R9
-    self.Emit("MOVQ" , _VAR_sv_p, _AX)                          // MOVQ    _VAR_sv_p, AX
-    self.Emit("MOVQ" , jit.Ptr(_DI, _Fe_Name), _CX)             // MOVQ    FieldEntry.Name(DI), CX
-    self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0))                    // MOVQ    AX, (SP)
-    self.Emit("MOVQ" , _CX, jit.Ptr(_SP, 8))                    // MOVQ    CX, 8(SP)
-    self.Emit("MOVQ" , _DX, jit.Ptr(_SP, 16))                   // MOVQ    DX, 16(SP)
-    self.call_go(_F_memequal)                                   // CALL_GO memequal
-    self.Emit("MOVQ" , _VAR_ss_AX, _AX)                         // MOVQ    ss.AX, AX
-    self.Emit("MOVQ" , _VAR_ss_CX, _CX)                         // MOVQ    ss.CX, CX
-    self.Emit("MOVQ" , _VAR_ss_SI, _SI)                         // MOVQ    ss.SI, SI
-    self.Emit("MOVQ" , _VAR_ss_R9, _R9)                         // MOVQ    ss.R9, R9
-    self.Emit("MOVB" , jit.Ptr(_SP, 24), _DX)                   // MOVB    24(SP), DX
-    self.Emit("TESTB", _DX, _DX)                                // TESTB   DX, DX
-    self.Sjmp("JZ"   , "_loop_{n}")                             // JZ      _loop_{n}
-    self.Emit("MOVQ" , _VAR_ss_R8, _R8)                         // MOVQ    ss.R8, R8
-    self.Emit("MOVQ" , _R8, _VAR_sr)                            // MOVQ    R8, sr
-    self.Sjmp("JMP"  , "_end_{n}")                              // JMP     _end_{n}
-    self.Link("_try_lowercase_{n}")                             // _try_lowercase_{n}:
-    self.Emit("MOVQ" , jit.Imm(referenceFields(p.vf())), _AX)   // MOVQ    ${p.vf()}, AX
-    self.Emit("MOVOU", _VAR_sv, _X0)                            // MOVOU   sv, X0
-    self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0))                    // MOVQ    AX, (SP)
-    self.Emit("MOVOU", _X0, jit.Ptr(_SP, 8))                    // MOVOU   X0, 8(SP)
-    self.call_go(_F_FieldMap_GetCaseInsensitive)                // CALL_GO FieldMap::GetCaseInsensitive
-    self.Emit("MOVQ" , jit.Ptr(_SP, 24), _AX)                   // MOVQ    24(SP), AX
-    self.Emit("MOVQ" , _AX, _VAR_sr)                            // MOVQ    AX, _VAR_sr
-    self.Emit("TESTQ", _AX, _AX)                                // TESTQ   AX, AX
-    self.Sjmp("JNS"  , "_end_{n}")                              // JNS     _end_{n}
-    self.Emit("BTQ"  , jit.Imm(_F_disable_unknown), _ARG_fv)    // BTQ     ${_F_disable_unknown}, fv
-    self.Sjmp("JC"   , _LB_field_error)                         // JC      _field_error
-    self.Link("_end_{n}")                                       // _end_{n}:
-}
-
-func (self *_Assembler) _asm_OP_unmarshal(p *_Instr) {
-    self.unmarshal_json(p.vt(), true)
-}
-
-func (self *_Assembler) _asm_OP_unmarshal_p(p *_Instr) {
-    self.unmarshal_json(p.vt(), false)
-}
-
-func (self *_Assembler) _asm_OP_unmarshal_text(p *_Instr) {
-    self.unmarshal_text(p.vt(), true)
-}
-
-func (self *_Assembler) _asm_OP_unmarshal_text_p(p *_Instr) {
-    self.unmarshal_text(p.vt(), false)
-}
-
-func (self *_Assembler) _asm_OP_lspace(_ *_Instr) {
-    self.lspace("_{n}")
-}
-
-func (self *_Assembler) lspace(subfix string) {
-    var label = "_lspace" + subfix
-
-    self.Emit("CMPQ"   , _IC, _IL)                      // CMPQ    IC, IL
-    self.Sjmp("JAE"    , _LB_eof_error)                 // JAE     _eof_error
-    self.Emit("MOVQ"   , jit.Imm(_BM_space), _DX)       // MOVQ    _BM_space, DX
-    self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX)  // MOVBQZX (IP)(IC), AX
-    self.Emit("CMPQ"   , _AX, jit.Imm(' '))             // CMPQ    AX, $' '
-    self.Sjmp("JA"     , label)                // JA      _nospace_{n}
-    self.Emit("BTQ"    , _AX, _DX)                      // BTQ     AX, DX
-    self.Sjmp("JNC"    , label)                // JNC     _nospace_{n}
-
-    /* test up to 4 characters */
-    for i := 0; i < 3; i++ {
-        self.Emit("ADDQ"   , jit.Imm(1), _IC)               // ADDQ    $1, IC
-        self.Emit("CMPQ"   , _IC, _IL)                      // CMPQ    IC, IL
-        self.Sjmp("JAE"    , _LB_eof_error)                 // JAE     _eof_error
-        self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX)  // MOVBQZX (IP)(IC), AX
-        self.Emit("CMPQ"   , _AX, jit.Imm(' '))             // CMPQ    AX, $' '
-        self.Sjmp("JA"     , label)                // JA      _nospace_{n}
-        self.Emit("BTQ"    , _AX, _DX)                      // BTQ     AX, DX
-        self.Sjmp("JNC"    , label)                // JNC     _nospace_{n}
-    }
-
-    /* handle over to the native function */
-    self.Emit("MOVQ"   , _IP, _DI)                      // MOVQ    IP, DI
-    self.Emit("MOVQ"   , _IL, _SI)                      // MOVQ    IL, SI
-    self.Emit("MOVQ"   , _IC, _DX)                      // MOVQ    IC, DX
-    self.call(_F_lspace)                                // CALL    lspace
-    self.Emit("TESTQ"  , _AX, _AX)                      // TESTQ   AX, AX
-    self.Sjmp("JS"     , _LB_parsing_error_v)           // JS      _parsing_error_v
-    self.Emit("CMPQ"   , _AX, _IL)                      // CMPQ    AX, IL
-    self.Sjmp("JAE"    , _LB_eof_error)                 // JAE     _eof_error
-    self.Emit("MOVQ"   , _AX, _IC)                      // MOVQ    AX, IC
-    self.Link(label)                           // _nospace_{n}:
-}
-
-func (self *_Assembler) _asm_OP_match_char(p *_Instr) {
-    self.match_char(p.vb())
-}
-
-func (self *_Assembler) match_char(char byte) {
-    self.check_eof(1)
-    self.Emit("CMPB", jit.Sib(_IP, _IC, 1, 0), jit.Imm(int64(char)))  // CMPB (IP)(IC), ${p.vb()}
-    self.Sjmp("JNE" , _LB_char_0_error)                               // JNE  _char_0_error
-    self.Emit("ADDQ", jit.Imm(1), _IC)                                // ADDQ $1, IC
-}
-
-func (self *_Assembler) _asm_OP_check_char(p *_Instr) {
-    self.check_eof(1)
-    self.Emit("LEAQ"   , jit.Ptr(_IC, 1), _AX)                              // LEAQ    1(IC), AX
-    self.Emit("CMPB"   , jit.Sib(_IP, _IC, 1, 0), jit.Imm(int64(p.vb())))   // CMPB    (IP)(IC), ${p.vb()}
-    self.Emit("CMOVQEQ", _AX, _IC)                                          // CMOVQEQ AX, IC
-    self.Xjmp("JE"     , p.vi())                                            // JE      {p.vi()}
-}
-
-func (self *_Assembler) _asm_OP_check_char_0(p *_Instr) {
-    self.check_eof(1)
-    self.Emit("CMPB", jit.Sib(_IP, _IC, 1, 0), jit.Imm(int64(p.vb())))   // CMPB    (IP)(IC), ${p.vb()}
-    self.Xjmp("JE"  , p.vi())                                            // JE      {p.vi()}
-}
-
-func (self *_Assembler) _asm_OP_add(p *_Instr) {
-    self.Emit("ADDQ", jit.Imm(int64(p.vi())), _IC)  // ADDQ ${p.vi()}, IC
-}
-
-func (self *_Assembler) _asm_OP_load(_ *_Instr) {
-    self.Emit("MOVQ", jit.Ptr(_ST, 0), _AX)             // MOVQ (ST), AX
-    self.Emit("MOVQ", jit.Sib(_ST, _AX, 1, 0), _VP)     // MOVQ (ST)(AX), VP
-}
-
-func (self *_Assembler) _asm_OP_save(_ *_Instr) {
-    self.Emit("MOVQ", jit.Ptr(_ST, 0), _CX)             // MOVQ (ST), CX
-    self.Emit("CMPQ", _CX, jit.Imm(_MaxStackBytes))          // CMPQ CX, ${_MaxStackBytes}
-    self.Sjmp("JAE"  , _LB_stack_error)                 // JA   _stack_error
-    self.WriteRecNotAX(0 , _VP, jit.Sib(_ST, _CX, 1, 8), false, false) // MOVQ VP, 8(ST)(CX)
-    self.Emit("ADDQ", jit.Imm(8), _CX)                  // ADDQ $8, CX
-    self.Emit("MOVQ", _CX, jit.Ptr(_ST, 0))             // MOVQ CX, (ST)
-}
-
-func (self *_Assembler) _asm_OP_drop(_ *_Instr) {
-    self.Emit("MOVQ", jit.Ptr(_ST, 0), _AX)             // MOVQ (ST), AX
-    self.Emit("SUBQ", jit.Imm(8), _AX)                  // SUBQ $8, AX
-    self.Emit("MOVQ", jit.Sib(_ST, _AX, 1, 8), _VP)     // MOVQ 8(ST)(AX), VP
-    self.Emit("MOVQ", _AX, jit.Ptr(_ST, 0))             // MOVQ AX, (ST)
-    self.Emit("XORL", _ET, _ET)                         // XORL ET, ET
-    self.Emit("MOVQ", _ET, jit.Sib(_ST, _AX, 1, 8))     // MOVQ ET, 8(ST)(AX)
-}
-
-func (self *_Assembler) _asm_OP_drop_2(_ *_Instr) {
-    self.Emit("MOVQ" , jit.Ptr(_ST, 0), _AX)            // MOVQ  (ST), AX
-    self.Emit("SUBQ" , jit.Imm(16), _AX)                // SUBQ  $16, AX
-    self.Emit("MOVQ" , jit.Sib(_ST, _AX, 1, 8), _VP)    // MOVQ  8(ST)(AX), VP
-    self.Emit("MOVQ" , _AX, jit.Ptr(_ST, 0))            // MOVQ  AX, (ST)
-    self.Emit("PXOR" , _X0, _X0)                        // PXOR  X0, X0
-    self.Emit("MOVOU", _X0, jit.Sib(_ST, _AX, 1, 8))    // MOVOU X0, 8(ST)(AX)
-}
-
-func (self *_Assembler) _asm_OP_recurse(p *_Instr) {
-    self.Emit("MOVQ", jit.Type(p.vt()), _AX)    // MOVQ   ${p.vt()}, AX
-    self.decode_dynamic(_AX, _VP)               // DECODE AX, VP
-}
-
-func (self *_Assembler) _asm_OP_goto(p *_Instr) {
-    self.Xjmp("JMP", p.vi())
-}
-
-func (self *_Assembler) _asm_OP_switch(p *_Instr) {
-    self.Emit("MOVQ", _VAR_sr, _AX)             // MOVQ sr, AX
-    self.Emit("CMPQ", _AX, jit.Imm(p.i64()))    // CMPQ AX, ${len(p.vs())}
-    self.Sjmp("JAE" , "_default_{n}")           // JAE  _default_{n}
-
-    /* jump table selector */
-    self.Byte(0x48, 0x8d, 0x3d)                         // LEAQ    ?(PC), DI
-    self.Sref("_switch_table_{n}", 4)                   // ....    &_switch_table_{n}
-    self.Emit("MOVLQSX", jit.Sib(_DI, _AX, 4, 0), _AX)  // MOVLQSX (DI)(AX*4), AX
-    self.Emit("ADDQ"   , _DI, _AX)                      // ADDQ    DI, AX
-    self.Rjmp("JMP"    , _AX)                           // JMP     AX
-    self.Link("_switch_table_{n}")                      // _switch_table_{n}:
-
-    /* generate the jump table */
-    for i, v := range p.vs() {
-        self.Xref(v, int64(-i) * 4)
-    }
-
-    /* default case */
-    self.Link("_default_{n}")
-    self.NOP()
-}
-
-func (self *_Assembler) print_gc(i int, p1 *_Instr, p2 *_Instr) {
-    self.Emit("MOVQ", jit.Imm(int64(p2.op())),  jit.Ptr(_SP, 16))// MOVQ $(p2.op()), 16(SP)
-    self.Emit("MOVQ", jit.Imm(int64(p1.op())),  jit.Ptr(_SP, 8)) // MOVQ $(p1.op()), 8(SP)
-    self.Emit("MOVQ", jit.Imm(int64(i)),  jit.Ptr(_SP, 0))       // MOVQ $(i), (SP)
-    self.call_go(_F_println)
-}

+ 37 - 0
vendor/github.com/bytedance/sonic/internal/decoder/consts/option.go

@@ -0,0 +1,37 @@
+
+package consts
+
+import (
+    `github.com/bytedance/sonic/internal/native/types`
+)
+
+
+const (
+    F_use_int64       = 0
+    F_disable_urc     = 2
+    F_disable_unknown = 3
+    F_copy_string     = 4
+
+    F_use_number      = types.B_USE_NUMBER
+    F_validate_string = types.B_VALIDATE_STRING
+    F_allow_control   = types.B_ALLOW_CONTROL
+    F_no_validate_json = types.B_NO_VALIDATE_JSON
+    F_case_sensitive = 7
+)
+
+type Options uint64
+
+const (
+    OptionUseInt64         Options = 1 << F_use_int64
+    OptionUseNumber        Options = 1 << F_use_number
+    OptionUseUnicodeErrors Options = 1 << F_disable_urc
+    OptionDisableUnknown   Options = 1 << F_disable_unknown
+    OptionCopyString       Options = 1 << F_copy_string
+    OptionValidateString   Options = 1 << F_validate_string
+    OptionNoValidateJSON   Options = 1 << F_no_validate_json
+    OptionCaseSensitive    Options = 1 << F_case_sensitive
+)
+
+const (
+	MaxStack = 4096
+)

+ 8 - 8
vendor/github.com/bytedance/sonic/internal/decoder/errors.go → vendor/github.com/bytedance/sonic/internal/decoder/errors/errors.go

@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package decoder
+package errors
 
 import (
     `encoding/json`
@@ -46,7 +46,7 @@ func (self SyntaxError) Description() string {
 func (self SyntaxError) description() string {
     /* check for empty source */
     if self.Src == "" {
-        return fmt.Sprintf("no sources available: %#v", self)
+        return fmt.Sprintf("no sources available, the input json is empty: %#v", self)
     }
 
     p, x, q, y := calcBounds(len(self.Src), self.Pos)
@@ -112,12 +112,12 @@ func clamp_zero(v int) int {
 
 /** JIT Error Helpers **/
 
-var stackOverflow = &json.UnsupportedValueError {
+var StackOverflow = &json.UnsupportedValueError {
     Str   : "Value nesting too deep",
     Value : reflect.ValueOf("..."),
 }
 
-func error_wrap(src string, pos int, code types.ParsingError) error {
+func ErrorWrap(src string, pos int, code types.ParsingError) error {
     return *error_wrap_heap(src, pos, code)
 }
 
@@ -130,7 +130,7 @@ func error_wrap_heap(src string, pos int, code types.ParsingError) *SyntaxError
     }
 }
 
-func error_type(vt *rt.GoType) error {
+func ErrorType(vt *rt.GoType) error {
     return &json.UnmarshalTypeError{Type: vt.Pack()}
 }
 
@@ -171,7 +171,7 @@ func (self MismatchTypeError) Description() string {
     return fmt.Sprintf("Mismatch type %s with value %s %s", self.Type.String(), swithchJSONType(self.Src, self.Pos), se.description())
 }
 
-func error_mismatch(src string, pos int, vt *rt.GoType) error {
+func ErrorMismatch(src string, pos int, vt *rt.GoType) error {
     return &MismatchTypeError {
         Pos  : pos,
         Src  : src,
@@ -179,11 +179,11 @@ func error_mismatch(src string, pos int, vt *rt.GoType) error {
     }
 }
 
-func error_field(name string) error {
+func ErrorField(name string) error {
     return errors.New("json: unknown field " + strconv.Quote(name))
 }
 
-func error_value(value string, vtype reflect.Type) error {
+func ErrorValue(value string, vtype reflect.Type) error {
     return &json.UnmarshalTypeError {
         Type  : vtype,
         Value : value,

+ 0 - 733
vendor/github.com/bytedance/sonic/internal/decoder/generic_stkabi_amd64.go

@@ -1,733 +0,0 @@
-// +build go1.16,!go1.17
-
-/*
- * Copyright 2021 ByteDance Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package decoder
-
-import (
-    `encoding/json`
-    `fmt`
-    `reflect`
-
-    `github.com/bytedance/sonic/internal/jit`
-    `github.com/bytedance/sonic/internal/native`
-    `github.com/bytedance/sonic/internal/native/types`
-    `github.com/twitchyliquid64/golang-asm/obj`
-)
-
-/** Crucial Registers:
- *
- *      ST(BX)  : ro, decoder stack
- *      DF(R10) : ro, decoder flags
- *      EP(R11) : wo, error pointer
- *      IP(R12) : ro, input pointer
- *      IL(R13) : ro, input length
- *      IC(R14) : rw, input cursor
- *      VP(R15) : ro, value pointer (to an interface{})
- */
-
-const (
-    _VD_args   = 8      // 8 bytes  for passing arguments to this functions
-    _VD_fargs  = 64     // 64 bytes for passing arguments to other Go functions
-    _VD_saves  = 40     // 40 bytes for saving the registers before CALL instructions
-    _VD_locals = 88     // 88 bytes for local variables
-)
-
-const (
-    _VD_offs = _VD_fargs + _VD_saves + _VD_locals
-    _VD_size = _VD_offs + 8     // 8 bytes for the parent frame pointer
-)
-
-var (
-    _VAR_ss = _VAR_ss_Vt
-    _VAR_df = jit.Ptr(_SP, _VD_fargs + _VD_saves)
-)
-
-var (
-    _VAR_ss_Vt = jit.Ptr(_SP, _VD_fargs + _VD_saves + 8)
-    _VAR_ss_Dv = jit.Ptr(_SP, _VD_fargs + _VD_saves + 16)
-    _VAR_ss_Iv = jit.Ptr(_SP, _VD_fargs + _VD_saves + 24)
-    _VAR_ss_Ep = jit.Ptr(_SP, _VD_fargs + _VD_saves + 32)
-    _VAR_ss_Db = jit.Ptr(_SP, _VD_fargs + _VD_saves + 40)
-    _VAR_ss_Dc = jit.Ptr(_SP, _VD_fargs + _VD_saves + 48)
-)
-
-var (
-    _VAR_cs_LR = jit.Ptr(_SP, _VD_fargs + _VD_saves + 56)
-    _VAR_cs_p = jit.Ptr(_SP, _VD_fargs + _VD_saves + 64)
-    _VAR_cs_n = jit.Ptr(_SP, _VD_fargs + _VD_saves + 72)
-    _VAR_cs_d = jit.Ptr(_SP, _VD_fargs + _VD_saves + 80)
-)
-
-type _ValueDecoder struct {
-    jit.BaseAssembler
-}
-
-func (self *_ValueDecoder) build() uintptr {
-    self.Init(self.compile)
-    return *(*uintptr)(self.Load("decode_value", _VD_size, _VD_args, argPtrs_generic, localPtrs_generic))
-}
-
-/** Function Calling Helpers **/
-
-func (self *_ValueDecoder) save(r ...obj.Addr) {
-    for i, v := range r {
-        if i > _VD_saves / 8 - 1 {
-            panic("too many registers to save")
-        } else {
-            self.Emit("MOVQ", v, jit.Ptr(_SP, _VD_fargs + int64(i) * 8))
-        }
-    }
-}
-
-func (self *_ValueDecoder) load(r ...obj.Addr) {
-    for i, v := range r {
-        if i > _VD_saves / 8 - 1 {
-            panic("too many registers to load")
-        } else {
-            self.Emit("MOVQ", jit.Ptr(_SP, _VD_fargs + int64(i) * 8), v)
-        }
-    }
-}
-
-func (self *_ValueDecoder) call(fn obj.Addr) {
-    self.Emit("MOVQ", fn, _AX)  // MOVQ ${fn}, AX
-    self.Rjmp("CALL", _AX)      // CALL AX
-}
-
-func (self *_ValueDecoder) call_go(fn obj.Addr) {
-    self.save(_REG_go...)   // SAVE $REG_go
-    self.call(fn)           // CALL ${fn}
-    self.load(_REG_go...)   // LOAD $REG_go
-}
-
-/** Decoder Assembler **/
-
-const (
-    _S_val = iota + 1
-    _S_arr
-    _S_arr_0
-    _S_obj
-    _S_obj_0
-    _S_obj_delim
-    _S_obj_sep
-)
-
-const (
-    _S_omask_key = (1 << _S_obj_0) | (1 << _S_obj_sep)
-    _S_omask_end = (1 << _S_obj_0) | (1 << _S_obj)
-    _S_vmask = (1 << _S_val) | (1 << _S_arr_0)
-)
-
-const (
-    _A_init_len = 1
-    _A_init_cap = 16
-)
-
-const (
-    _ST_Sp = 0
-    _ST_Vt = _PtrBytes
-    _ST_Vp = _PtrBytes * (types.MAX_RECURSE + 1)
-)
-
-var (
-    _V_true  = jit.Imm(int64(pbool(true)))
-    _V_false = jit.Imm(int64(pbool(false)))
-    _F_value = jit.Imm(int64(native.S_value))
-)
-
-var (
-    _V_max     = jit.Imm(int64(types.V_MAX))
-    _E_eof     = jit.Imm(int64(types.ERR_EOF))
-    _E_invalid = jit.Imm(int64(types.ERR_INVALID_CHAR))
-    _E_recurse = jit.Imm(int64(types.ERR_RECURSE_EXCEED_MAX))
-)
-
-var (
-    _F_convTslice    = jit.Func(convTslice)
-    _F_convTstring   = jit.Func(convTstring)
-    _F_invalid_vtype = jit.Func(invalid_vtype)
-)
-
-var (
-    _T_map     = jit.Type(reflect.TypeOf((map[string]interface{})(nil)))
-    _T_bool    = jit.Type(reflect.TypeOf(false))
-    _T_int64   = jit.Type(reflect.TypeOf(int64(0)))
-    _T_eface   = jit.Type(reflect.TypeOf((*interface{})(nil)).Elem())
-    _T_slice   = jit.Type(reflect.TypeOf(([]interface{})(nil)))
-    _T_string  = jit.Type(reflect.TypeOf(""))
-    _T_number  = jit.Type(reflect.TypeOf(json.Number("")))
-    _T_float64 = jit.Type(reflect.TypeOf(float64(0)))
-)
-
-var _R_tab = map[int]string {
-    '[': "_decode_V_ARRAY",
-    '{': "_decode_V_OBJECT",
-    ':': "_decode_V_KEY_SEP",
-    ',': "_decode_V_ELEM_SEP",
-    ']': "_decode_V_ARRAY_END",
-    '}': "_decode_V_OBJECT_END",
-}
-
-func (self *_ValueDecoder) compile() {
-    self.Emit("SUBQ", jit.Imm(_VD_size), _SP)       // SUBQ $_VD_size, SP
-    self.Emit("MOVQ", _BP, jit.Ptr(_SP, _VD_offs))  // MOVQ BP, _VD_offs(SP)
-    self.Emit("LEAQ", jit.Ptr(_SP, _VD_offs), _BP)  // LEAQ _VD_offs(SP), BP
-
-    /* initialize the state machine */
-    self.Emit("XORL", _CX, _CX)                                 // XORL CX, CX
-    self.Emit("MOVQ", _DF, _VAR_df)                             // MOVQ DF, df
-    /* initialize digital buffer first */
-    self.Emit("MOVQ", jit.Imm(_MaxDigitNums), _VAR_ss_Dc)       // MOVQ $_MaxDigitNums, ss.Dcap
-    self.Emit("LEAQ", jit.Ptr(_ST, _DbufOffset), _AX)           // LEAQ _DbufOffset(ST), AX
-    self.Emit("MOVQ", _AX, _VAR_ss_Db)                          // MOVQ AX, ss.Dbuf
-    /* add ST offset */
-    self.Emit("ADDQ", jit.Imm(_FsmOffset), _ST)                 // ADDQ _FsmOffset, _ST
-    self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp))                // MOVQ CX, ST.Sp
-    self.WriteRecNotAX(0, _VP, jit.Ptr(_ST, _ST_Vp), false)                // MOVQ VP, ST.Vp[0]
-    self.Emit("MOVQ", jit.Imm(_S_val), jit.Ptr(_ST, _ST_Vt))    // MOVQ _S_val, ST.Vt[0]
-    self.Sjmp("JMP" , "_next")                                  // JMP  _next
-
-    /* set the value from previous round */
-    self.Link("_set_value")                                 // _set_value:
-    self.Emit("MOVL" , jit.Imm(_S_vmask), _DX)              // MOVL  _S_vmask, DX
-    self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _CX)           // MOVQ  ST.Sp, CX
-    self.Emit("MOVQ" , jit.Sib(_ST, _CX, 8, _ST_Vt), _AX)   // MOVQ  ST.Vt[CX], AX
-    self.Emit("BTQ"  , _AX, _DX)                            // BTQ   AX, DX
-    self.Sjmp("JNC"  , "_vtype_error")                      // JNC   _vtype_error
-    self.Emit("XORL" , _SI, _SI)                            // XORL  SI, SI
-    self.Emit("SUBQ" , jit.Imm(1), jit.Ptr(_ST, _ST_Sp))    // SUBQ  $1, ST.Sp
-    self.Emit("XCHGQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI)   // XCHGQ ST.Vp[CX], SI
-    self.Emit("MOVQ" , _R8, jit.Ptr(_SI, 0))                // MOVQ  R8, (SI)
-    self.WriteRecNotAX(1, _R9, jit.Ptr(_SI, 8), false)           // MOVQ  R9, 8(SI)
-
-    /* check for value stack */
-    self.Link("_next")                              // _next:
-    self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _AX)   // MOVQ  ST.Sp, AX
-    self.Emit("TESTQ", _AX, _AX)                    // TESTQ AX, AX
-    self.Sjmp("JS"   , "_return")                   // JS    _return
-
-    /* fast path: test up to 4 characters manually */
-    self.Emit("CMPQ"   , _IC, _IL)                      // CMPQ    IC, IL
-    self.Sjmp("JAE"    , "_decode_V_EOF")               // JAE     _decode_V_EOF
-    self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX)  // MOVBQZX (IP)(IC), AX
-    self.Emit("MOVQ"   , jit.Imm(_BM_space), _DX)       // MOVQ    _BM_space, DX
-    self.Emit("CMPQ"   , _AX, jit.Imm(' '))             // CMPQ    AX, $' '
-    self.Sjmp("JA"     , "_decode_fast")                // JA      _decode_fast
-    self.Emit("BTQ"    , _AX, _DX)                      // BTQ     _AX, _DX
-    self.Sjmp("JNC"    , "_decode_fast")                // JNC     _decode_fast
-    self.Emit("ADDQ"   , jit.Imm(1), _IC)               // ADDQ    $1, IC
-
-    /* at least 1 to 3 spaces */
-    for i := 0; i < 3; i++ {
-        self.Emit("CMPQ"   , _IC, _IL)                      // CMPQ    IC, IL
-        self.Sjmp("JAE"    , "_decode_V_EOF")               // JAE     _decode_V_EOF
-        self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX)  // MOVBQZX (IP)(IC), AX
-        self.Emit("CMPQ"   , _AX, jit.Imm(' '))             // CMPQ    AX, $' '
-        self.Sjmp("JA"     , "_decode_fast")                // JA      _decode_fast
-        self.Emit("BTQ"    , _AX, _DX)                      // BTQ     _AX, _DX
-        self.Sjmp("JNC"    , "_decode_fast")                // JNC     _decode_fast
-        self.Emit("ADDQ"   , jit.Imm(1), _IC)               // ADDQ    $1, IC
-    }
-
-    /* at least 4 spaces */
-    self.Emit("CMPQ"   , _IC, _IL)                      // CMPQ    IC, IL
-    self.Sjmp("JAE"    , "_decode_V_EOF")               // JAE     _decode_V_EOF
-    self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX)  // MOVBQZX (IP)(IC), AX
-
-    /* fast path: use lookup table to select decoder */
-    self.Link("_decode_fast")                           // _decode_fast:
-    self.Byte(0x48, 0x8d, 0x3d)                         // LEAQ    ?(PC), DI
-    self.Sref("_decode_tab", 4)                         // ....    &_decode_tab
-    self.Emit("MOVLQSX", jit.Sib(_DI, _AX, 4, 0), _AX)  // MOVLQSX (DI)(AX*4), AX
-    self.Emit("TESTQ"  , _AX, _AX)                      // TESTQ   AX, AX
-    self.Sjmp("JZ"     , "_decode_native")              // JZ      _decode_native
-    self.Emit("ADDQ"   , jit.Imm(1), _IC)               // ADDQ    $1, IC
-    self.Emit("ADDQ"   , _DI, _AX)                      // ADDQ    DI, AX
-    self.Rjmp("JMP"    , _AX)                           // JMP     AX
-
-    /* decode with native decoder */
-    self.Link("_decode_native")         // _decode_native:
-    self.Emit("MOVQ", _IP, _DI)         // MOVQ IP, DI
-    self.Emit("MOVQ", _IL, _SI)         // MOVQ IL, SI
-    self.Emit("MOVQ", _IC, _DX)         // MOVQ IC, DX
-    self.Emit("LEAQ", _VAR_ss, _CX)     // LEAQ ss, CX
-    self.Emit("MOVQ", _VAR_df, _R8)     // MOVQ $df, R8
-    self.Emit("BTSQ", jit.Imm(_F_allow_control), _R8)  // ANDQ $1<<_F_allow_control, R8
-    self.call(_F_value)                 // CALL value
-    self.Emit("MOVQ", _AX, _IC)         // MOVQ AX, IC
-
-    /* check for errors */
-    self.Emit("MOVQ" , _VAR_ss_Vt, _AX)     // MOVQ  ss.Vt, AX
-    self.Emit("TESTQ", _AX, _AX)            // TESTQ AX, AX
-    self.Sjmp("JS"   , "_parsing_error")
-    self.Sjmp("JZ"   , "_invalid_vtype")    // JZ    _invalid_vtype
-    self.Emit("CMPQ" , _AX, _V_max)         // CMPQ  AX, _V_max
-    self.Sjmp("JA"   , "_invalid_vtype")    // JA    _invalid_vtype
-
-    /* jump table selector */
-    self.Byte(0x48, 0x8d, 0x3d)                             // LEAQ    ?(PC), DI
-    self.Sref("_switch_table", 4)                           // ....    &_switch_table
-    self.Emit("MOVLQSX", jit.Sib(_DI, _AX, 4, -4), _AX)     // MOVLQSX -4(DI)(AX*4), AX
-    self.Emit("ADDQ"   , _DI, _AX)                          // ADDQ    DI, AX
-    self.Rjmp("JMP"    , _AX)                               // JMP     AX
-
-    /** V_EOF **/
-    self.Link("_decode_V_EOF")          // _decode_V_EOF:
-    self.Emit("MOVL", _E_eof, _EP)      // MOVL _E_eof, EP
-    self.Sjmp("JMP" , "_error")         // JMP  _error
-
-    /** V_NULL **/
-    self.Link("_decode_V_NULL")                 // _decode_V_NULL:
-    self.Emit("XORL", _R8, _R8)                 // XORL R8, R8
-    self.Emit("XORL", _R9, _R9)                 // XORL R9, R9
-    self.Emit("LEAQ", jit.Ptr(_IC, -4), _DI)    // LEAQ -4(IC), DI
-    self.Sjmp("JMP" , "_set_value")             // JMP  _set_value
-
-    /** V_TRUE **/
-    self.Link("_decode_V_TRUE")                 // _decode_V_TRUE:
-    self.Emit("MOVQ", _T_bool, _R8)             // MOVQ _T_bool, R8
-    // TODO: maybe modified by users?
-    self.Emit("MOVQ", _V_true, _R9)             // MOVQ _V_true, R9 
-    self.Emit("LEAQ", jit.Ptr(_IC, -4), _DI)    // LEAQ -4(IC), DI
-    self.Sjmp("JMP" , "_set_value")             // JMP  _set_value
-
-    /** V_FALSE **/
-    self.Link("_decode_V_FALSE")                // _decode_V_FALSE:
-    self.Emit("MOVQ", _T_bool, _R8)             // MOVQ _T_bool, R8
-    self.Emit("MOVQ", _V_false, _R9)            // MOVQ _V_false, R9
-    self.Emit("LEAQ", jit.Ptr(_IC, -5), _DI)    // LEAQ -5(IC), DI
-    self.Sjmp("JMP" , "_set_value")             // JMP  _set_value
-
-    /** V_ARRAY **/
-    self.Link("_decode_V_ARRAY")                            // _decode_V_ARRAY
-    self.Emit("MOVL", jit.Imm(_S_vmask), _DX)               // MOVL _S_vmask, DX
-    self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX)            // MOVQ ST.Sp, CX
-    self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX)    // MOVQ ST.Vt[CX], AX
-    self.Emit("BTQ" , _AX, _DX)                             // BTQ  AX, DX
-    self.Sjmp("JNC" , "_invalid_char")                      // JNC  _invalid_char
-
-    /* create a new array */
-    self.Emit("MOVQ", _T_eface, _AX)                            // MOVQ    _T_eface, AX
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0))                     // MOVQ    AX, (SP)
-    self.Emit("MOVQ", jit.Imm(_A_init_len), jit.Ptr(_SP, 8))    // MOVQ    _A_init_len, 8(SP)
-    self.Emit("MOVQ", jit.Imm(_A_init_cap), jit.Ptr(_SP, 16))   // MOVQ    _A_init_cap, 16(SP)
-    self.call_go(_F_makeslice)                                  // CALL_GO runtime.makeslice
-    self.Emit("MOVQ", jit.Ptr(_SP, 24), _DX)                    // MOVQ    24(SP), DX
-
-    /* pack into an interface */
-    self.Emit("MOVQ", _DX, jit.Ptr(_SP, 0))                     // MOVQ    DX, (SP)
-    self.Emit("MOVQ", jit.Imm(_A_init_len), jit.Ptr(_SP, 8))    // MOVQ    _A_init_len, 8(SP)
-    self.Emit("MOVQ", jit.Imm(_A_init_cap), jit.Ptr(_SP, 16))   // MOVQ    _A_init_cap, 16(SP)
-    self.call_go(_F_convTslice)                                 // CALL_GO runtime.convTslice
-    self.Emit("MOVQ", jit.Ptr(_SP, 24), _R8)                    // MOVQ    24(SP), R8
-
-    /* replace current state with an array */
-    self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX)                        // MOVQ ST.Sp, CX
-    self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI)                // MOVQ ST.Vp[CX], SI
-    self.Emit("MOVQ", jit.Imm(_S_arr), jit.Sib(_ST, _CX, 8, _ST_Vt))    // MOVQ _S_arr, ST.Vt[CX]
-    self.Emit("MOVQ", _T_slice, _AX)                                    // MOVQ _T_slice, AX
-    self.Emit("MOVQ", _AX, jit.Ptr(_SI, 0))                             // MOVQ AX, (SI)
-    self.WriteRecNotAX(2, _R8, jit.Ptr(_SI, 8), false)                            // MOVQ R8, 8(SI)
-
-    /* add a new slot for the first element */
-    self.Emit("ADDQ", jit.Imm(1), _CX)                                  // ADDQ $1, CX
-    self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE))                  // CMPQ CX, ${types.MAX_RECURSE}
-    self.Sjmp("JAE"  , "_stack_overflow")                                // JA   _stack_overflow
-    self.Emit("MOVQ", jit.Ptr(_R8, 0), _AX)                             // MOVQ (R8), AX
-    self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp))                        // MOVQ CX, ST.Sp
-    self.WritePtrAX(3, jit.Sib(_ST, _CX, 8, _ST_Vp), false)                // MOVQ AX, ST.Vp[CX]
-    self.Emit("MOVQ", jit.Imm(_S_arr_0), jit.Sib(_ST, _CX, 8, _ST_Vt))  // MOVQ _S_arr_0, ST.Vt[CX]
-    self.Sjmp("JMP" , "_next")                                          // JMP  _next
-
-    /** V_OBJECT **/
-    self.Link("_decode_V_OBJECT")                                       // _decode_V_OBJECT:
-    self.Emit("MOVL", jit.Imm(_S_vmask), _DX)                           // MOVL    _S_vmask, DX
-    self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX)                        // MOVQ    ST.Sp, CX
-    self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX)                // MOVQ    ST.Vt[CX], AX
-    self.Emit("BTQ" , _AX, _DX)                                         // BTQ     AX, DX
-    self.Sjmp("JNC" , "_invalid_char")                                  // JNC     _invalid_char
-    self.call_go(_F_makemap_small)                                      // CALL_GO runtime.makemap_small
-    self.Emit("MOVQ", jit.Ptr(_SP, 0), _AX)                             // MOVQ    (SP), AX
-    self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX)                        // MOVQ    ST.Sp, CX
-    self.Emit("MOVQ", jit.Imm(_S_obj_0), jit.Sib(_ST, _CX, 8, _ST_Vt))    // MOVQ    _S_obj, ST.Vt[CX]
-    self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI)                // MOVQ    ST.Vp[CX], SI
-    self.Emit("MOVQ", _T_map, _DX)                                      // MOVQ    _T_map, DX
-    self.Emit("MOVQ", _DX, jit.Ptr(_SI, 0))                             // MOVQ    DX, (SI)
-    self.WritePtrAX(4, jit.Ptr(_SI, 8), false)                             // MOVQ    AX, 8(SI)
-    self.Sjmp("JMP" , "_next")                                          // JMP     _next
-
-    /** V_STRING **/
-    self.Link("_decode_V_STRING")       // _decode_V_STRING:
-    self.Emit("MOVQ", _VAR_ss_Iv, _CX)  // MOVQ ss.Iv, CX
-    self.Emit("MOVQ", _IC, _AX)         // MOVQ IC, AX
-    self.Emit("SUBQ", _CX, _AX)         // SUBQ CX, AX
-
-    /* check for escapes */
-    self.Emit("CMPQ", _VAR_ss_Ep, jit.Imm(-1))          // CMPQ ss.Ep, $-1
-    self.Sjmp("JNE" , "_unquote")                       // JNE  _unquote
-    self.Emit("SUBQ", jit.Imm(1), _AX)                  // SUBQ $1, AX
-    self.Emit("LEAQ", jit.Sib(_IP, _CX, 1, 0), _R8)     // LEAQ (IP)(CX), R8
-    self.Byte(0x48, 0x8d, 0x3d)                         // LEAQ (PC), DI
-    self.Sref("_copy_string_end", 4)
-    self.Emit("BTQ", jit.Imm(_F_copy_string), _VAR_df)
-    self.Sjmp("JC", "copy_string")
-    self.Link("_copy_string_end")                                 
-    self.Emit("XORL", _DX, _DX)                             // XORL DX, DX
-    /* strings with no escape sequences */
-    self.Link("_noescape")                                  // _noescape:
-    self.Emit("MOVL", jit.Imm(_S_omask_key), _DI)               // MOVL _S_omask, DI
-    self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX)            // MOVQ ST.Sp, CX
-    self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _SI)    // MOVQ ST.Vt[CX], SI
-    self.Emit("BTQ" , _SI, _DI)                             // BTQ  SI, DI
-    self.Sjmp("JC"  , "_object_key")                        // JC   _object_key
-
-    /* check for pre-packed strings, avoid 1 allocation */
-    self.Emit("TESTQ", _DX, _DX)                // TESTQ   DX, DX
-    self.Sjmp("JNZ"  , "_packed_str")           // JNZ     _packed_str
-    self.Emit("MOVQ" , _R8, jit.Ptr(_SP, 0))    // MOVQ    R8, (SP)
-    self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 8))    // MOVQ    AX, 8(SP)
-    self.call_go(_F_convTstring)                // CALL_GO runtime.convTstring
-    self.Emit("MOVQ" , jit.Ptr(_SP, 16), _R9)   // MOVQ    16(SP), R9
-
-    /* packed string already in R9 */
-    self.Link("_packed_str")            // _packed_str:
-    self.Emit("MOVQ", _T_string, _R8)   // MOVQ _T_string, R8
-    self.Emit("MOVQ", _VAR_ss_Iv, _DI)  // MOVQ ss.Iv, DI
-    self.Emit("SUBQ", jit.Imm(1), _DI)  // SUBQ $1, DI
-    self.Sjmp("JMP" , "_set_value")     // JMP  _set_value
-
-    /* the string is an object key, get the map */
-    self.Link("_object_key")
-    self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX)            // MOVQ ST.Sp, CX
-    self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI)    // MOVQ ST.Vp[CX], SI
-    self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI)                 // MOVQ 8(SI), SI
-
-    /* add a new delimiter */
-    self.Emit("ADDQ", jit.Imm(1), _CX)                                      // ADDQ $1, CX
-    self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE))                      // CMPQ CX, ${types.MAX_RECURSE}
-    self.Sjmp("JAE"  , "_stack_overflow")                                    // JA   _stack_overflow
-    self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp))                            // MOVQ CX, ST.Sp
-    self.Emit("MOVQ", jit.Imm(_S_obj_delim), jit.Sib(_ST, _CX, 8, _ST_Vt))  // MOVQ _S_obj_delim, ST.Vt[CX]
-
-    /* add a new slot int the map */
-    self.Emit("MOVQ", _T_map, _DX)                      // MOVQ    _T_map, DX
-    self.Emit("MOVQ", _DX, jit.Ptr(_SP, 0))             // MOVQ    DX, (SP)
-    self.Emit("MOVQ", _SI, jit.Ptr(_SP, 8))             // MOVQ    SI, 8(SP)
-    self.Emit("MOVQ", _R8, jit.Ptr(_SP, 16))            // MOVQ    R9, 16(SP)
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 24))            // MOVQ    AX, 24(SP)
-    self.call_go(_F_mapassign_faststr)                  // CALL_GO runtime.mapassign_faststr
-    self.Emit("MOVQ", jit.Ptr(_SP, 32), _AX)            // MOVQ    32(SP), AX
-
-    /* add to the pointer stack */
-    self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX)                 // MOVQ ST.Sp, CX
-    self.WritePtrAX(6, jit.Sib(_ST, _CX, 8, _ST_Vp), false)    // MOVQ AX, ST.Vp[CX]
-    self.Sjmp("JMP" , "_next")                                   // JMP  _next
-
-    /* allocate memory to store the string header and unquoted result */
-    self.Link("_unquote")                               // _unquote:
-    self.Emit("ADDQ", jit.Imm(15), _AX)                 // ADDQ    $15, AX
-    self.Emit("MOVQ", _T_byte, _CX)                     // MOVQ    _T_byte, CX
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0))             // MOVQ    AX, (SP)
-    self.Emit("MOVQ", _CX, jit.Ptr(_SP, 8))             // MOVQ    CX, 8(SP)
-    self.Emit("MOVB", jit.Imm(0), jit.Ptr(_SP, 16))     // MOVB    $0, 16(SP)
-    self.call_go(_F_mallocgc)                           // CALL_GO runtime.mallocgc
-    self.Emit("MOVQ", jit.Ptr(_SP, 24), _R9)            // MOVQ    24(SP), R9
-
-    /* prepare the unquoting parameters */
-    self.Emit("MOVQ" , _VAR_ss_Iv, _CX)                         // MOVQ  ss.Iv, CX
-    self.Emit("LEAQ" , jit.Sib(_IP, _CX, 1, 0), _DI)            // LEAQ  (IP)(CX), DI
-    self.Emit("NEGQ" , _CX)                                     // NEGQ  CX
-    self.Emit("LEAQ" , jit.Sib(_IC, _CX, 1, -1), _SI)           // LEAQ  -1(IC)(CX), SI
-    self.Emit("LEAQ" , jit.Ptr(_R9, 16), _DX)                   // LEAQ  16(R8), DX
-    self.Emit("LEAQ" , _VAR_ss_Ep, _CX)                         // LEAQ  ss.Ep, CX
-    self.Emit("XORL" , _R8, _R8)                                // XORL  R8, R8
-    self.Emit("BTQ"  , jit.Imm(_F_disable_urc), _VAR_df)        // BTQ   ${_F_disable_urc}, fv
-    self.Emit("SETCC", _R8)                                     // SETCC R8
-    self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _R8)   // SHLQ  ${types.B_UNICODE_REPLACE}, R8
-
-    /* unquote the string, with R9 been preserved */
-    self.save(_R9)                                      // SAVE R9
-    self.call(_F_unquote)                               // CALL unquote
-    self.load(_R9)                                      // LOAD R9
-
-    /* check for errors */
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ AX, AX
-    self.Sjmp("JS"   , "_unquote_error")        // JS    _unquote_error
-    self.Emit("MOVL" , jit.Imm(1), _DX)         // MOVL  $1, DX
-    self.Emit("LEAQ" , jit.Ptr(_R9, 16), _R8)   // ADDQ  $16, R8
-    self.Emit("MOVQ" , _R8, jit.Ptr(_R9, 0))    // MOVQ  R8, (R9)
-    self.Emit("MOVQ" , _AX, jit.Ptr(_R9, 8))    // MOVQ  AX, 8(R9)
-    self.Sjmp("JMP"  , "_noescape")             // JMP   _noescape
-
-    /** V_DOUBLE **/
-    self.Link("_decode_V_DOUBLE")                           // _decode_V_DOUBLE:
-    self.Emit("BTQ"  , jit.Imm(_F_use_number), _VAR_df)     // BTQ     _F_use_number, df
-    self.Sjmp("JC"   , "_use_number")                       // JC      _use_number
-    self.Emit("MOVSD", _VAR_ss_Dv, _X0)                     // MOVSD   ss.Dv, X0
-    self.Sjmp("JMP"  , "_use_float64")                      // JMP     _use_float64
-
-    /** V_INTEGER **/
-    self.Link("_decode_V_INTEGER")                          // _decode_V_INTEGER:
-    self.Emit("BTQ"     , jit.Imm(_F_use_number), _VAR_df)  // BTQ      _F_use_number, df
-    self.Sjmp("JC"      , "_use_number")                    // JC       _use_number
-    self.Emit("BTQ"     , jit.Imm(_F_use_int64), _VAR_df)   // BTQ      _F_use_int64, df
-    self.Sjmp("JC"      , "_use_int64")                     // JC       _use_int64
-    self.Emit("MOVQ"    , _VAR_ss_Iv, _AX)                  // MOVQ     ss.Iv, AX
-    self.Emit("CVTSQ2SD", _AX, _X0)                         // CVTSQ2SD AX, X0
-
-    /* represent numbers as `float64` */
-    self.Link("_use_float64")                   // _use_float64:
-    self.Emit("MOVSD", _X0, jit.Ptr(_SP, 0))    // MOVSD   X0, (SP)
-    self.call_go(_F_convT64)                    // CALL_GO runtime.convT64
-    self.Emit("MOVQ" , _T_float64, _R8)         // MOVQ    _T_float64, R8
-    self.Emit("MOVQ" , jit.Ptr(_SP, 8), _R9)    // MOVQ    8(SP), R9
-    self.Emit("MOVQ" , _VAR_ss_Ep, _DI)         // MOVQ    ss.Ep, DI
-    self.Sjmp("JMP"  , "_set_value")            // JMP     _set_value
-
-    /* represent numbers as `json.Number` */
-    self.Link("_use_number")                            // _use_number
-    self.Emit("MOVQ", _VAR_ss_Ep, _AX)                  // MOVQ    ss.Ep, AX
-    self.Emit("LEAQ", jit.Sib(_IP, _AX, 1, 0), _SI)     // LEAQ    (IP)(AX), SI
-    self.Emit("MOVQ", _IC, _CX)                         // MOVQ    IC, CX
-    self.Emit("SUBQ", _AX, _CX)                         // SUBQ    AX, CX
-    self.Emit("MOVQ", _SI, jit.Ptr(_SP, 0))             // MOVQ    SI, (SP)
-    self.Emit("MOVQ", _CX, jit.Ptr(_SP, 8))             // MOVQ    CX, 8(SP)
-    self.call_go(_F_convTstring)                        // CALL_GO runtime.convTstring
-    self.Emit("MOVQ", _T_number, _R8)                   // MOVQ    _T_number, R8
-    self.Emit("MOVQ", jit.Ptr(_SP, 16), _R9)            // MOVQ    16(SP), R9
-    self.Emit("MOVQ", _VAR_ss_Ep, _DI)                  // MOVQ    ss.Ep, DI
-    self.Sjmp("JMP" , "_set_value")                     // JMP     _set_value
-
-    /* represent numbers as `int64` */
-    self.Link("_use_int64")                     // _use_int64:
-    self.Emit("MOVQ", _VAR_ss_Iv, _AX)          // MOVQ    ss.Iv, AX
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0))     // MOVQ    AX, (SP)
-    self.call_go(_F_convT64)                    // CALL_GO runtime.convT64
-    self.Emit("MOVQ", _T_int64, _R8)            // MOVQ    _T_int64, R8
-    self.Emit("MOVQ", jit.Ptr(_SP, 8), _R9)     // MOVQ    8(SP), R9
-    self.Emit("MOVQ", _VAR_ss_Ep, _DI)          // MOVQ    ss.Ep, DI
-    self.Sjmp("JMP" , "_set_value")             // JMP     _set_value
-
-    /** V_KEY_SEP **/
-    self.Link("_decode_V_KEY_SEP")                                          // _decode_V_KEY_SEP:
-    // self.Byte(0xcc)
-    self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX)                            // MOVQ ST.Sp, CX
-    self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX)                    // MOVQ ST.Vt[CX], AX
-    self.Emit("CMPQ", _AX, jit.Imm(_S_obj_delim))                           // CMPQ AX, _S_obj_delim
-    self.Sjmp("JNE" , "_invalid_char")                                      // JNE  _invalid_char
-    self.Emit("MOVQ", jit.Imm(_S_val), jit.Sib(_ST, _CX, 8, _ST_Vt))        // MOVQ _S_val, ST.Vt[CX]
-    self.Emit("MOVQ", jit.Imm(_S_obj), jit.Sib(_ST, _CX, 8, _ST_Vt - 8))    // MOVQ _S_obj, ST.Vt[CX - 1]
-    self.Sjmp("JMP" , "_next")                                              // JMP  _next
-
-    /** V_ELEM_SEP **/
-    self.Link("_decode_V_ELEM_SEP")                             // _decode_V_ELEM_SEP:
-    self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _CX)            // MOVQ     ST.Sp, CX
-    self.Emit("MOVQ" , jit.Sib(_ST, _CX, 8, _ST_Vt), _AX)    // MOVQ     ST.Vt[CX], AX
-    self.Emit("CMPQ" , _AX, jit.Imm(_S_arr))                 // CMPQ     _AX, _S_arr
-    self.Sjmp("JE"   , "_array_sep")                         // JZ       _next
-    self.Emit("CMPQ" , _AX, jit.Imm(_S_obj))                 // CMPQ     _AX, _S_arr
-    self.Sjmp("JNE"  , "_invalid_char")                      // JNE      _invalid_char
-    self.Emit("MOVQ" , jit.Imm(_S_obj_sep), jit.Sib(_ST, _CX, 8, _ST_Vt))
-    self.Sjmp("JMP"  , "_next")                              // JMP      _next
-
-    /* arrays */
-    self.Link("_array_sep")
-    self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI)    // MOVQ ST.Vp[CX], SI
-    self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI)                 // MOVQ 8(SI), SI
-    self.Emit("MOVQ", jit.Ptr(_SI, 8), _DX)                 // MOVQ 8(SI), DX
-    self.Emit("CMPQ", _DX, jit.Ptr(_SI, 16))                // CMPQ DX, 16(SI)
-    self.Sjmp("JAE" , "_array_more")                        // JAE  _array_more
-
-    /* add a slot for the new element */
-    self.Link("_array_append")                                          // _array_append:
-    self.Emit("ADDQ", jit.Imm(1), jit.Ptr(_SI, 8))                      // ADDQ $1, 8(SI)
-    self.Emit("MOVQ", jit.Ptr(_SI, 0), _SI)                             // MOVQ (SI), SI
-    self.Emit("ADDQ", jit.Imm(1), _CX)                                  // ADDQ $1, CX
-    self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE))                  // CMPQ CX, ${types.MAX_RECURSE}
-    self.Sjmp("JAE"  , "_stack_overflow") 
-    self.Emit("SHLQ", jit.Imm(1), _DX)                                  // SHLQ $1, DX
-    self.Emit("LEAQ", jit.Sib(_SI, _DX, 8, 0), _SI)                     // LEAQ (SI)(DX*8), SI
-    self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp))                        // MOVQ CX, ST.Sp
-    self.WriteRecNotAX(7 , _SI, jit.Sib(_ST, _CX, 8, _ST_Vp), false)           // MOVQ SI, ST.Vp[CX]
-    self.Emit("MOVQ", jit.Imm(_S_val), jit.Sib(_ST, _CX, 8, _ST_Vt))    // MOVQ _S_val, ST.Vt[CX}
-    self.Sjmp("JMP" , "_next")                                          // JMP  _next
-
-    /** V_ARRAY_END **/
-    self.Link("_decode_V_ARRAY_END")                        // _decode_V_ARRAY_END:
-    self.Emit("XORL", _DX, _DX)                             // XORL DX, DX
-    self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX)            // MOVQ ST.Sp, CX
-    self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX)    // MOVQ ST.Vt[CX], AX
-    self.Emit("CMPQ", _AX, jit.Imm(_S_arr_0))               // CMPQ AX, _S_arr_0
-    self.Sjmp("JE"  , "_first_item")                        // JE   _first_item
-    self.Emit("CMPQ", _AX, jit.Imm(_S_arr))                 // CMPQ AX, _S_arr
-    self.Sjmp("JNE" , "_invalid_char")                      // JNE  _invalid_char
-    self.Emit("SUBQ", jit.Imm(1), jit.Ptr(_ST, _ST_Sp))     // SUBQ $1, ST.Sp
-    self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp))    // MOVQ DX, ST.Vp[CX]
-    self.Sjmp("JMP" , "_next")                              // JMP  _next
-
-    /* first element of an array */
-    self.Link("_first_item")                                    // _first_item:
-    self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX)                // MOVQ ST.Sp, CX
-    self.Emit("SUBQ", jit.Imm(2), jit.Ptr(_ST, _ST_Sp))         // SUBQ $2, ST.Sp
-    self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp - 8), _SI)    // MOVQ ST.Vp[CX - 1], SI
-    self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI)                     // MOVQ 8(SI), SI
-    self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp - 8))    // MOVQ DX, ST.Vp[CX - 1]
-    self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp))        // MOVQ DX, ST.Vp[CX]
-    self.Emit("MOVQ", _DX, jit.Ptr(_SI, 8))                     // MOVQ DX, 8(SI)
-    self.Sjmp("JMP" , "_next")                                  // JMP  _next
-
-    /** V_OBJECT_END **/
-    self.Link("_decode_V_OBJECT_END")                       // _decode_V_OBJECT_END:
-    self.Emit("MOVL", jit.Imm(_S_omask_end), _DX)           // MOVL _S_omask, DI
-    self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX)            // MOVQ ST.Sp, CX
-    self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX)    // MOVQ ST.Vt[CX], AX
-    self.Emit("BTQ" , _AX, _DX)                
-    self.Sjmp("JNC" , "_invalid_char")                      // JNE  _invalid_char
-    self.Emit("XORL", _AX, _AX)                             // XORL AX, AX
-    self.Emit("SUBQ", jit.Imm(1), jit.Ptr(_ST, _ST_Sp))     // SUBQ $1, ST.Sp
-    self.Emit("MOVQ", _AX, jit.Sib(_ST, _CX, 8, _ST_Vp))    // MOVQ AX, ST.Vp[CX]
-    self.Sjmp("JMP" , "_next")                              // JMP  _next
-
-    /* return from decoder */
-    self.Link("_return")                            // _return:
-    self.Emit("XORL", _EP, _EP)                     // XORL EP, EP
-    self.Emit("MOVQ", _EP, jit.Ptr(_ST, _ST_Vp))    // MOVQ EP, ST.Vp[0]
-    self.Link("_epilogue")                          // _epilogue:
-    self.Emit("SUBQ", jit.Imm(_FsmOffset), _ST)     // SUBQ _FsmOffset, _ST
-    self.Emit("MOVQ", jit.Ptr(_SP, _VD_offs), _BP)  // MOVQ _VD_offs(SP), BP
-    self.Emit("ADDQ", jit.Imm(_VD_size), _SP)       // ADDQ $_VD_size, SP
-    self.Emit("RET")                                // RET
-
-    /* array expand */
-    self.Link("_array_more")                    // _array_more:
-    self.Emit("MOVQ" , _T_eface, _AX)           // MOVQ    _T_eface, AX
-    self.Emit("MOVOU", jit.Ptr(_SI, 0), _X0)    // MOVOU   (SI), X0
-    self.Emit("MOVQ" , jit.Ptr(_SI, 16), _DX)   // MOVQ    16(SI), DX
-    self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0))    // MOVQ    AX, (SP)
-    self.Emit("MOVOU", _X0, jit.Ptr(_SP, 8))    // MOVOU   X0, 8(SP)
-    self.Emit("MOVQ" , _DX, jit.Ptr(_SP, 24))   // MOVQ    DX, 24(SP)
-    self.Emit("SHLQ" , jit.Imm(1), _DX)         // SHLQ    $1, DX
-    self.Emit("MOVQ" , _DX, jit.Ptr(_SP, 32))   // MOVQ    DX, 32(SP)
-    self.call_go(_F_growslice)                  // CALL_GO runtime.growslice
-    self.Emit("MOVQ" , jit.Ptr(_SP, 40), _DI)   // MOVOU   40(SP), DI
-    self.Emit("MOVQ" , jit.Ptr(_SP, 48), _DX)   // MOVOU   48(SP), DX
-    self.Emit("MOVQ" , jit.Ptr(_SP, 56), _AX)   // MOVQ    56(SP), AX
-
-    /* update the slice */
-    self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX)            // MOVQ ST.Sp, CX
-    self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI)    // MOVQ ST.Vp[CX], SI
-    self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI)                 // MOVQ 8(SI), SI
-    self.Emit("MOVQ", _DX, jit.Ptr(_SI, 8))                 // MOVQ DX, 8(SI)
-    self.Emit("MOVQ", _AX, jit.Ptr(_SI, 16))                // MOVQ AX, 16(AX)
-    self.WriteRecNotAX(8 , _DI, jit.Ptr(_SI, 0), false)                 // MOVQ R10, (SI)
-    self.Sjmp("JMP" , "_array_append")                      // JMP  _array_append
-
-    /* copy string */
-    self.Link("copy_string")  // pointer: R8, length: AX, return addr: DI
-    // self.Byte(0xcc)
-    self.Emit("MOVQ", _R8, _VAR_cs_p)
-    self.Emit("MOVQ", _AX, _VAR_cs_n)
-    self.Emit("MOVQ", _DI, _VAR_cs_LR)
-    self.Emit("MOVQ", _T_byte, _R8)
-    self.Emit("MOVQ", _R8, jit.Ptr(_SP, 0))
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 8))
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 16))
-    self.call_go(_F_makeslice)                              
-    self.Emit("MOVQ", jit.Ptr(_SP, 24), _R8)      
-    self.Emit("MOVQ", _R8, _VAR_cs_d)                    
-    self.Emit("MOVQ", _R8, jit.Ptr(_SP, 0))                    
-    self.Emit("MOVQ", _VAR_cs_p, _R8)
-    self.Emit("MOVQ", _R8, jit.Ptr(_SP, 8))
-    self.Emit("MOVQ", _VAR_cs_n, _AX)
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 16))
-    self.call_go(_F_memmove)
-    self.Emit("MOVQ", _VAR_cs_d, _R8)
-    self.Emit("MOVQ", _VAR_cs_n, _AX)
-    self.Emit("MOVQ", _VAR_cs_LR, _DI)
-    // self.Byte(0xcc)
-    self.Rjmp("JMP", _DI)
-
-    /* error handlers */
-    self.Link("_stack_overflow")
-    self.Emit("MOVL" , _E_recurse, _EP)         // MOVQ  _E_recurse, EP
-    self.Sjmp("JMP"  , "_error")                // JMP   _error
-    self.Link("_vtype_error")                   // _vtype_error:
-    self.Emit("MOVQ" , _DI, _IC)                // MOVQ  DI, IC
-    self.Emit("MOVL" , _E_invalid, _EP)         // MOVL  _E_invalid, EP
-    self.Sjmp("JMP"  , "_error")                // JMP   _error
-    self.Link("_invalid_char")                  // _invalid_char:
-    self.Emit("SUBQ" , jit.Imm(1), _IC)         // SUBQ  $1, IC
-    self.Emit("MOVL" , _E_invalid, _EP)         // MOVL  _E_invalid, EP
-    self.Sjmp("JMP"  , "_error")                // JMP   _error
-    self.Link("_unquote_error")                 // _unquote_error:
-    self.Emit("MOVQ" , _VAR_ss_Iv, _IC)         // MOVQ  ss.Iv, IC
-    self.Emit("SUBQ" , jit.Imm(1), _IC)         // SUBQ  $1, IC
-    self.Link("_parsing_error")                 // _parsing_error:
-    self.Emit("NEGQ" , _AX)                     // NEGQ  AX
-    self.Emit("MOVQ" , _AX, _EP)                // MOVQ  AX, EP
-    self.Link("_error")                         // _error:
-    self.Emit("PXOR" , _X0, _X0)                // PXOR  X0, X0
-    self.Emit("MOVOU", _X0, jit.Ptr(_VP, 0))    // MOVOU X0, (VP)
-    self.Sjmp("JMP"  , "_epilogue")             // JMP   _epilogue
-
-    /* invalid value type, never returns */
-    self.Link("_invalid_vtype")
-    self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0))     // MOVQ AX, (SP)
-    self.call(_F_invalid_vtype)                 // CALL invalid_type
-    self.Emit("UD2")                            // UD2
-
-    /* switch jump table */
-    self.Link("_switch_table")              // _switch_table:
-    self.Sref("_decode_V_EOF", 0)           // SREF &_decode_V_EOF, $0
-    self.Sref("_decode_V_NULL", -4)         // SREF &_decode_V_NULL, $-4
-    self.Sref("_decode_V_TRUE", -8)         // SREF &_decode_V_TRUE, $-8
-    self.Sref("_decode_V_FALSE", -12)       // SREF &_decode_V_FALSE, $-12
-    self.Sref("_decode_V_ARRAY", -16)       // SREF &_decode_V_ARRAY, $-16
-    self.Sref("_decode_V_OBJECT", -20)      // SREF &_decode_V_OBJECT, $-20
-    self.Sref("_decode_V_STRING", -24)      // SREF &_decode_V_STRING, $-24
-    self.Sref("_decode_V_DOUBLE", -28)      // SREF &_decode_V_DOUBLE, $-28
-    self.Sref("_decode_V_INTEGER", -32)     // SREF &_decode_V_INTEGER, $-32
-    self.Sref("_decode_V_KEY_SEP", -36)     // SREF &_decode_V_KEY_SEP, $-36
-    self.Sref("_decode_V_ELEM_SEP", -40)    // SREF &_decode_V_ELEM_SEP, $-40
-    self.Sref("_decode_V_ARRAY_END", -44)   // SREF &_decode_V_ARRAY_END, $-44
-    self.Sref("_decode_V_OBJECT_END", -48)  // SREF &_decode_V_OBJECT_END, $-48
-
-    /* fast character lookup table */
-    self.Link("_decode_tab")        // _decode_tab:
-    self.Sref("_decode_V_EOF", 0)   // SREF &_decode_V_EOF, $0
-
-    /* generate rest of the tabs */
-    for i := 1; i < 256; i++ {
-        if to, ok := _R_tab[i]; ok {
-            self.Sref(to, -int64(i) * 4)
-        } else {
-            self.Byte(0x00, 0x00, 0x00, 0x00)
-        }
-    }
-}
-
-/** Generic Decoder **/
-
-var (
-    _subr_decode_value = new(_ValueDecoder).build()
-)
-
-//go:nosplit
-func invalid_vtype(vt types.ValueType) {
-    throw(fmt.Sprintf("invalid value type: %d", vt))
-}

+ 0 - 0
vendor/github.com/bytedance/sonic/internal/decoder/asm.s → vendor/github.com/bytedance/sonic/internal/decoder/jitdec/asm.s


+ 4 - 9
vendor/github.com/bytedance/sonic/internal/decoder/asm_stubs_amd64_go117.go → vendor/github.com/bytedance/sonic/internal/decoder/jitdec/asm_stubs_amd64_go117.go

@@ -14,27 +14,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package decoder
+package jitdec
 
 import (
     `strconv`
     `unsafe`
 
+    `github.com/bytedance/sonic/internal/rt`
     `github.com/bytedance/sonic/internal/jit`
     `github.com/twitchyliquid64/golang-asm/obj`
     `github.com/twitchyliquid64/golang-asm/obj/x86`
 )
 
-//go:linkname _runtime_writeBarrier runtime.writeBarrier
-var _runtime_writeBarrier uintptr
-
-//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier
-func gcWriteBarrierAX()
-
 var (
-    _V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&_runtime_writeBarrier))))
+    _V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&rt.RuntimeWriteBarrier))))
 
-    _F_gcWriteBarrierAX = jit.Func(gcWriteBarrierAX)
+    _F_gcWriteBarrierAX = jit.Func(rt.GcWriteBarrierAX)
 )
 
 func (self *_Assembler) WritePtrAX(i int, rec obj.Addr, saveDI bool) {

+ 5 - 11
vendor/github.com/bytedance/sonic/internal/decoder/asm_stubs_amd64_go121.go → vendor/github.com/bytedance/sonic/internal/decoder/jitdec/asm_stubs_amd64_go121.go

@@ -1,4 +1,4 @@
-// +build go1.21,!go1.23
+// +build go1.21,!go1.25
 
 // Copyright 2023 CloudWeGo Authors
 //
@@ -14,31 +14,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package decoder
+package jitdec
 
 import (
     `strconv`
     `unsafe`
 
+    `github.com/bytedance/sonic/internal/rt`
     `github.com/bytedance/sonic/internal/jit`
     `github.com/twitchyliquid64/golang-asm/obj`
     `github.com/twitchyliquid64/golang-asm/obj/x86`
 )
 
-//go:linkname _runtime_writeBarrier runtime.writeBarrier
-var _runtime_writeBarrier uintptr
-
-//go:nosplit
-//go:linkname gcWriteBarrier2 runtime.gcWriteBarrier2
-func gcWriteBarrier2()
-
 // Notice: gcWriteBarrier must use R11 register!!
 var _R11 = _IC
 
 var (
-    _V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&_runtime_writeBarrier))))
+    _V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&rt.RuntimeWriteBarrier))))
 
-    _F_gcWriteBarrier2 = jit.Func(gcWriteBarrier2)
+    _F_gcWriteBarrier2 = jit.Func(rt.GcWriteBarrier2)
 )
 
 func (self *_Assembler) WritePtrAX(i int, rec obj.Addr, saveDI bool) {

+ 139 - 57
vendor/github.com/bytedance/sonic/internal/decoder/assembler_regabi_amd64.go → vendor/github.com/bytedance/sonic/internal/decoder/jitdec/assembler_regabi_amd64.go

@@ -1,4 +1,5 @@
-// +build go1.17,!go1.23
+//go:build go1.17 && !go1.25
+// +build go1.17,!go1.25
 
 /*
  * Copyright 2021 ByteDance Inc.
@@ -16,21 +17,22 @@
  * limitations under the License.
  */
 
-package decoder
+package jitdec
 
 import (
-    `encoding/json`
-    `fmt`
-    `math`
-    `reflect`
-    `unsafe`
-
-    `github.com/bytedance/sonic/internal/caching`
-    `github.com/bytedance/sonic/internal/jit`
-    `github.com/bytedance/sonic/internal/native`
-    `github.com/bytedance/sonic/internal/native/types`
-    `github.com/bytedance/sonic/internal/rt`
-    `github.com/twitchyliquid64/golang-asm/obj`
+	"encoding/json"
+	"fmt"
+	"math"
+	"reflect"
+	"strings"
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/caching"
+	"github.com/bytedance/sonic/internal/jit"
+	"github.com/bytedance/sonic/internal/native"
+	"github.com/bytedance/sonic/internal/native/types"
+	"github.com/bytedance/sonic/internal/rt"
+	"github.com/twitchyliquid64/golang-asm/obj"
 )
 
 /** Register Allocations
@@ -65,7 +67,7 @@ import (
  */
 
 const (
-    _FP_args   = 72     // 72 bytes to pass and spill register arguements
+    _FP_args   = 72     // 72 bytes to pass and spill register arguments
     _FP_fargs  = 80     // 80 bytes for passing arguments to other Go functions
     _FP_saves  = 48     // 48 bytes for saving the registers before CALL instructions
     _FP_locals = 144    // 144 bytes for local variables
@@ -201,9 +203,9 @@ var (
 var _VAR_fl = jit.Ptr(_SP, _FP_fargs + _FP_saves + 112)
 
 var (
-    _VAR_et = jit.Ptr(_SP, _FP_fargs + _FP_saves + 120) // save dismatched type
+    _VAR_et = jit.Ptr(_SP, _FP_fargs + _FP_saves + 120) // save mismatched type
     _VAR_pc = jit.Ptr(_SP, _FP_fargs + _FP_saves + 128) // save skip return pc
-    _VAR_ic = jit.Ptr(_SP, _FP_fargs + _FP_saves + 136) // save dismatched position
+    _VAR_ic = jit.Ptr(_SP, _FP_fargs + _FP_saves + 136) // save mismatched position
 )
 
 type _Assembler struct {
@@ -269,6 +271,7 @@ var _OpFuncTab = [256]func(*_Assembler, *_Instr) {
     _OP_nil_1            : (*_Assembler)._asm_OP_nil_1,
     _OP_nil_2            : (*_Assembler)._asm_OP_nil_2,
     _OP_nil_3            : (*_Assembler)._asm_OP_nil_3,
+    _OP_empty_bytes      : (*_Assembler)._asm_OP_empty_bytes,
     _OP_deref            : (*_Assembler)._asm_OP_deref,
     _OP_index            : (*_Assembler)._asm_OP_index,
     _OP_is_null          : (*_Assembler)._asm_OP_is_null,
@@ -292,7 +295,6 @@ var _OpFuncTab = [256]func(*_Assembler, *_Instr) {
     _OP_array_clear_p    : (*_Assembler)._asm_OP_array_clear_p,
     _OP_slice_init       : (*_Assembler)._asm_OP_slice_init,
     _OP_slice_append     : (*_Assembler)._asm_OP_slice_append,
-    _OP_object_skip      : (*_Assembler)._asm_OP_object_skip,
     _OP_object_next      : (*_Assembler)._asm_OP_object_next,
     _OP_struct_field     : (*_Assembler)._asm_OP_struct_field,
     _OP_unmarshal        : (*_Assembler)._asm_OP_unmarshal,
@@ -312,8 +314,10 @@ var _OpFuncTab = [256]func(*_Assembler, *_Instr) {
     _OP_check_char_0     : (*_Assembler)._asm_OP_check_char_0,
     _OP_dismatch_err     : (*_Assembler)._asm_OP_dismatch_err,
     _OP_go_skip          : (*_Assembler)._asm_OP_go_skip,
+    _OP_skip_emtpy       : (*_Assembler)._asm_OP_skip_empty,
     _OP_add              : (*_Assembler)._asm_OP_add,
     _OP_check_empty      : (*_Assembler)._asm_OP_check_empty,
+    _OP_unsupported      : (*_Assembler)._asm_OP_unsupported,
     _OP_debug            : (*_Assembler)._asm_OP_debug,
 }
 
@@ -385,7 +389,7 @@ func (self *_Assembler) prologue() {
 
 var (
     _REG_go = []obj.Addr { _ST, _VP, _IP, _IL, _IC }
-    _REG_rt = []obj.Addr { _ST, _VP, _IP, _IL, _IC, _IL }
+    _REG_rt = []obj.Addr { _ST, _VP, _IP, _IL, _IC }
 )
 
 func (self *_Assembler) save(r ...obj.Addr) {
@@ -454,7 +458,7 @@ func (self *_Assembler) call_vf(fn obj.Addr) {
 /** Assembler Error Handlers **/
 
 var (
-    _F_convT64        = jit.Func(convT64)
+    _F_convT64        = jit.Func(rt.ConvT64)
     _F_error_wrap     = jit.Func(error_wrap)
     _F_error_type     = jit.Func(error_type)
     _F_error_field    = jit.Func(error_field)
@@ -481,6 +485,7 @@ var (
     _V_stackOverflow              = jit.Imm(int64(uintptr(unsafe.Pointer(&stackOverflow))))
     _I_json_UnsupportedValueError = jit.Itab(_T_error, reflect.TypeOf(new(json.UnsupportedValueError)))
     _I_json_MismatchTypeError     = jit.Itab(_T_error, reflect.TypeOf(new(MismatchTypeError)))
+    _I_json_MismatchQuotedError   = jit.Itab(_T_error, reflect.TypeOf(new(MismatchQuotedError)))
 )
 
 func (self *_Assembler) type_error() {
@@ -492,9 +497,9 @@ func (self *_Assembler) type_error() {
 func (self *_Assembler) mismatch_error() {
     self.Link(_LB_mismatch_error)                     // _type_error:
     self.Emit("MOVQ", _VAR_et, _ET)                   // MOVQ _VAR_et, ET
-    self.Emit("MOVQ", _VAR_ic, _EP)                   // MOVQ _VAR_ic, EP
     self.Emit("MOVQ", _I_json_MismatchTypeError, _CX) // MOVQ _I_json_MismatchType, CX
     self.Emit("CMPQ", _ET, _CX)                       // CMPQ ET, CX
+    self.Emit("MOVQ", jit.Ptr(_ST, _EpOffset), _EP)   // MOVQ stack.Ep, EP
     self.Sjmp("JE"  , _LB_error)                      // JE _LB_error
     self.Emit("MOVQ", _ARG_sp, _AX)
     self.Emit("MOVQ", _ARG_sl, _BX)
@@ -595,11 +600,29 @@ func (self *_Assembler) _asm_OP_dismatch_err(p *_Instr) {
 func (self *_Assembler) _asm_OP_go_skip(p *_Instr) {
     self.Byte(0x4c, 0x8d, 0x0d)         // LEAQ (PC), R9
     self.Xref(p.vi(), 4)
-    // self.Byte(0xcc)
     self.Emit("MOVQ", _R9, _VAR_pc)
     self.Sjmp("JMP"  , _LB_skip_one)            // JMP     _skip_one
 }
 
+var _F_IndexByte = jit.Func(strings.IndexByte)
+
+func (self *_Assembler) _asm_OP_skip_empty(p *_Instr) {
+    self.call_sf(_F_skip_one)                   // CALL_SF skip_one
+    self.Emit("TESTQ", _AX, _AX)                // TESTQ   AX, AX
+    self.Sjmp("JS"   , _LB_parsing_error_v)     // JS      _parse_error_v
+    self.Emit("BTQ", jit.Imm(_F_disable_unknown), _ARG_fv) 
+    self.Xjmp("JNC", p.vi())
+    self.Emit("LEAQ", jit.Sib(_IC, _AX, 1, 0), _BX)
+    self.Emit("MOVQ", _BX, _ARG_sv_n)
+    self.Emit("LEAQ", jit.Sib(_IP, _AX, 1, 0), _AX)
+    self.Emit("MOVQ", _AX, _ARG_sv_p)
+    self.Emit("MOVQ", jit.Imm(':'), _CX)
+    self.call_go(_F_IndexByte)
+    self.Emit("TESTQ", _AX, _AX)
+    // disallow unknown field
+    self.Sjmp("JNS", _LB_field_error)
+}
+
 func (self *_Assembler) skip_one() {
     self.Link(_LB_skip_one)                     // _skip:
     self.Emit("MOVQ", _VAR_ic, _IC)             // MOVQ    _VAR_ic, IC
@@ -637,7 +660,7 @@ func (self *_Assembler) skip_key_value() {
 
 var (
     _T_byte     = jit.Type(byteType)
-    _F_mallocgc = jit.Func(mallocgc)
+    _F_mallocgc = jit.Func(rt.Mallocgc)
 )
 
 func (self *_Assembler) malloc_AX(nb obj.Addr, ret obj.Addr) {
@@ -939,8 +962,8 @@ func (self *_Assembler) unquote_twice(p obj.Addr, n obj.Addr, stack bool) {
 /** Memory Clearing Routines **/
 
 var (
-    _F_memclrHasPointers    = jit.Func(memclrHasPointers)
-    _F_memclrNoHeapPointers = jit.Func(memclrNoHeapPointers)
+    _F_memclrHasPointers    = jit.Func(rt.MemclrHasPointers)
+    _F_memclrNoHeapPointers = jit.Func(rt.MemclrNoHeapPointers)
 )
 
 func (self *_Assembler) mem_clear_fn(ptrfree bool) {
@@ -964,19 +987,21 @@ func (self *_Assembler) mem_clear_rem(size int64, ptrfree bool) {
 /** Map Assigning Routines **/
 
 var (
-    _F_mapassign           = jit.Func(mapassign)
-    _F_mapassign_fast32    = jit.Func(mapassign_fast32)
-    _F_mapassign_faststr   = jit.Func(mapassign_faststr)
-    _F_mapassign_fast64ptr = jit.Func(mapassign_fast64ptr)
+    _F_mapassign           = jit.Func(rt.Mapassign)
+    _F_mapassign_fast32    = jit.Func(rt.Mapassign_fast32)
+    _F_mapassign_faststr   = jit.Func(rt.Mapassign_faststr)
+    _F_mapassign_fast64ptr = jit.Func(rt.Mapassign_fast64ptr)
 )
 
 var (
     _F_decodeJsonUnmarshaler obj.Addr
+    _F_decodeJsonUnmarshalerQuoted obj.Addr
     _F_decodeTextUnmarshaler obj.Addr
 )
 
 func init() {
     _F_decodeJsonUnmarshaler = jit.Func(decodeJsonUnmarshaler)
+    _F_decodeJsonUnmarshalerQuoted = jit.Func(decodeJsonUnmarshalerQuoted)
     _F_decodeTextUnmarshaler = jit.Func(decodeTextUnmarshaler)
 }
 
@@ -1057,18 +1082,18 @@ func (self *_Assembler) mapassign_utext(t reflect.Type, addressable bool) {
 var (
     _F_skip_one = jit.Imm(int64(native.S_skip_one))
     _F_skip_array  = jit.Imm(int64(native.S_skip_array))
-    _F_skip_object = jit.Imm(int64(native.S_skip_object))
     _F_skip_number = jit.Imm(int64(native.S_skip_number))
 )
 
-func (self *_Assembler) unmarshal_json(t reflect.Type, deref bool) {
+func (self *_Assembler) unmarshal_json(t reflect.Type, deref bool, f obj.Addr) {
     self.call_sf(_F_skip_one)                                   // CALL_SF   skip_one
     self.Emit("TESTQ", _AX, _AX)                                // TESTQ     AX, AX
     self.Sjmp("JS"   , _LB_parsing_error_v)                     // JS        _parse_error_v
+    self.Emit("MOVQ", _IC, _VAR_ic)                             // store for mismatche error skip
     self.slice_from_r(_AX, 0)                                   // SLICE_R   AX, $0
     self.Emit("MOVQ" , _DI, _ARG_sv_p)                          // MOVQ      DI, sv.p
     self.Emit("MOVQ" , _SI, _ARG_sv_n)                          // MOVQ      SI, sv.n
-    self.unmarshal_func(t, _F_decodeJsonUnmarshaler, deref)     // UNMARSHAL json, ${t}, ${deref}
+    self.unmarshal_func(t, f, deref)     // UNMARSHAL json, ${t}, ${deref}
 }
 
 func (self *_Assembler) unmarshal_text(t reflect.Type, deref bool) {
@@ -1103,7 +1128,19 @@ func (self *_Assembler) unmarshal_func(t reflect.Type, fn obj.Addr, deref bool)
     self.Emit("MOVQ" , _ARG_sv_n, _DI)          // MOVQ    sv.n, DI
     self.call_go(fn)                            // CALL_GO ${fn}
     self.Emit("TESTQ", _ET, _ET)                // TESTQ   ET, ET
-    self.Sjmp("JNZ"  , _LB_error)               // JNZ     _error
+    if fn == _F_decodeJsonUnmarshalerQuoted {
+        self.Sjmp("JZ"  , "_unmarshal_func_end_{n}")            // JZ   _unmarshal_func_end_{n}
+        self.Emit("MOVQ", _I_json_MismatchQuotedError, _CX)     // MOVQ _I_json_MismatchQuotedError, CX
+        self.Emit("CMPQ", _ET, _CX)            // check if MismatchQuotedError
+        self.Sjmp("JNE" , _LB_error)           // JNE     _error
+        self.Emit("MOVQ", jit.Type(t), _CX)    // store current type 
+        self.Emit("MOVQ", _CX, _VAR_et)        // store current type as mismatched type
+        self.Emit("MOVQ", _VAR_ic, _IC)        // recover the pos at mismatched, continue to parse
+        self.Emit("XORL", _ET, _ET)            // clear ET
+        self.Link("_unmarshal_func_end_{n}")
+    } else {
+        self.Sjmp("JNE" , _LB_error)           // JNE     _error
+    }
 }
 
 /** Dynamic Decoding Routine **/
@@ -1136,20 +1173,20 @@ func (self *_Assembler) decode_dynamic(vt obj.Addr, vp obj.Addr) {
     self.Emit("MOVQ", _I_json_MismatchTypeError, _CX) // MOVQ _I_json_MismatchTypeError, CX
     self.Emit("CMPQ", _ET, _CX)                 // CMPQ ET, CX
     self.Sjmp("JNE",  _LB_error)                // JNE  LB_error
-    self.Emit("MOVQ", _EP, _VAR_ic)             // MOVQ EP, VAR_ic
     self.Emit("MOVQ", _ET, _VAR_et)             // MOVQ ET, VAR_et
+    self.WriteRecNotAX(14, _EP, jit.Ptr(_ST, _EpOffset), false, false) // MOVQ EP, stack.Ep
     self.Link("_decode_dynamic_end_{n}")
 }
 
 /** OpCode Assembler Functions **/
 
 var (
-    _F_memequal         = jit.Func(memequal)
-    _F_memmove          = jit.Func(memmove)
-    _F_growslice        = jit.Func(growslice)
-    _F_makeslice        = jit.Func(makeslice)
-    _F_makemap_small    = jit.Func(makemap_small)
-    _F_mapassign_fast64 = jit.Func(mapassign_fast64)
+    _F_memequal         = jit.Func(rt.MemEqual)
+    _F_memmove          = jit.Func(rt.Memmove)
+    _F_growslice        = jit.Func(rt.GrowSlice)
+    _F_makeslice        = jit.Func(rt.MakeSliceStd)
+    _F_makemap_small    = jit.Func(rt.MakemapSmall)
+    _F_mapassign_fast64 = jit.Func(rt.Mapassign_fast64)
 )
 
 var (
@@ -1158,7 +1195,7 @@ var (
 )
 
 var (
-    _F_b64decode   = jit.Imm(int64(_subr__b64decode))
+    _F_b64decode   = jit.Imm(int64(rt.SubrB64Decode))
     _F_decodeValue = jit.Imm(int64(_subr_decode_value))
 )
 
@@ -1214,18 +1251,42 @@ func (self *_Assembler) _asm_OP_any(_ *_Instr) {
 func (self *_Assembler) _asm_OP_dyn(p *_Instr) {
     self.Emit("MOVQ"   , jit.Type(p.vt()), _ET)             // MOVQ    ${p.vt()}, ET
     self.Emit("CMPQ"   , jit.Ptr(_VP, 8), jit.Imm(0))       // CMPQ    8(VP), $0
-    self.Sjmp("JE"     , _LB_type_error)                    // JE      _type_error
+    self.Sjmp("JNE"     , "_decode_dyn_non_nil_{n}")                    // JE      _type_error
+
+    /* if nil iface, call skip one */
+    self.Emit("MOVQ", _IC, _VAR_ic)
+    self.Emit("MOVQ", _ET, _VAR_et)
+    self.Byte(0x4c, 0x8d, 0x0d)       
+    self.Sref("_decode_end_{n}", 4)
+    self.Emit("MOVQ", _R9, _VAR_pc)
+    self.Sjmp("JMP"  , _LB_skip_one)
+
+    self.Link("_decode_dyn_non_nil_{n}")                    // _decode_dyn_non_nil_{n}:
     self.Emit("MOVQ"   , jit.Ptr(_VP, 0), _CX)              // MOVQ    (VP), CX
     self.Emit("MOVQ"   , jit.Ptr(_CX, 8), _CX)              // MOVQ    8(CX), CX
     self.Emit("MOVBLZX", jit.Ptr(_CX, _Gt_KindFlags), _DX)  // MOVBLZX _Gt_KindFlags(CX), DX
     self.Emit("ANDL"   , jit.Imm(rt.F_kind_mask), _DX)      // ANDL    ${F_kind_mask}, DX
     self.Emit("CMPL"   , _DX, jit.Imm(_Vk_Ptr))             // CMPL    DX, ${reflect.Ptr}
-    self.Sjmp("JNE"    , _LB_type_error)                    // JNE     _type_error
+    self.Sjmp("JE"    , "_decode_dyn_ptr_{n}")              // JNE     _type_error
+
+    self.Emit("MOVQ", _IC, _VAR_ic)
+    self.Emit("MOVQ", _ET, _VAR_et)
+    self.Byte(0x4c, 0x8d, 0x0d)       
+    self.Sref("_decode_end_{n}", 4)
+    self.Emit("MOVQ", _R9, _VAR_pc)
+    self.Sjmp("JMP"  , _LB_skip_one)
+
+    self.Link("_decode_dyn_ptr_{n}")                        // _decode_dyn_ptr_{n}:
     self.Emit("LEAQ"   , jit.Ptr(_VP, 8), _DI)              // LEAQ    8(VP), DI
     self.decode_dynamic(_CX, _DI)                           // DECODE  CX, DI
     self.Link("_decode_end_{n}")                            // _decode_end_{n}:
 }
 
+func (self *_Assembler) _asm_OP_unsupported(p *_Instr) {
+    self.Emit("MOVQ", jit.Type(p.vt()), _ET)               // MOVQ    ${p.vt()}, ET
+    self.Sjmp("JMP" , _LB_type_error)                      // JMP     _LB_type_error
+}
+
 func (self *_Assembler) _asm_OP_str(_ *_Instr) {
     self.parse_string()                                     // PARSE   STRING
     self.unquote_once(jit.Ptr(_VP, 0), jit.Ptr(_VP, 8), false, true)     // UNQUOTE once, (VP), 8(VP)
@@ -1322,7 +1383,7 @@ func (self *_Assembler) _asm_OP_num(_ *_Instr) {
     self.Emit("MOVQ", _R9, _VAR_pc)
     self.Sjmp("JMP"  , _LB_skip_one)
 
-    /* assgin string */
+    /* assign string */
     self.Link("_num_next_{n}")
     self.slice_from_r(_AX, 0)
     self.Emit("BTQ", jit.Imm(_F_copy_string), _ARG_fv)
@@ -1449,6 +1510,19 @@ func (self *_Assembler) _asm_OP_nil_3(_ *_Instr) {
     self.Emit("MOVQ" , _AX, jit.Ptr(_VP, 16))   // MOVOU AX, 16(VP)
 }
 
+var (
+    bytes []byte = make([]byte, 0)
+    zerobytes = (*rt.GoSlice)(unsafe.Pointer(&bytes)).Ptr
+    _ZERO_PTR = jit.Imm(int64(uintptr(zerobytes)))
+)
+
+func (self *_Assembler) _asm_OP_empty_bytes(_ *_Instr) {
+    self.Emit("MOVQ", _ZERO_PTR, _AX)
+    self.Emit("PXOR" , _X0, _X0)
+    self.Emit("MOVQ", _AX,  jit.Ptr(_VP, 0))
+    self.Emit("MOVOU", _X0, jit.Ptr(_VP, 8))
+}
+
 func (self *_Assembler) _asm_OP_deref(p *_Instr) {
     self.vfollow(p.vt())
 }
@@ -1508,7 +1582,7 @@ func (self *_Assembler) _asm_OP_map_key_i32(p *_Instr) {
     self.parse_signed(int32Type, "", p.vi())                                                     // PARSE     int32
     self.range_signed_CX(_I_int32, _T_int32, math.MinInt32, math.MaxInt32)     // RANGE     int32
     self.match_char('"')
-    if vt := p.vt(); !mapfast(vt) {
+    if vt := p.vt(); !rt.IsMapfast(vt) {
         self.mapassign_std(vt, _VAR_st_Iv)                                  // MAPASSIGN int32, mapassign, st.Iv
     } else {
         self.Emit("MOVQ", _CX, _AX)                                         // MOVQ CX, AX
@@ -1519,7 +1593,7 @@ func (self *_Assembler) _asm_OP_map_key_i32(p *_Instr) {
 func (self *_Assembler) _asm_OP_map_key_i64(p *_Instr) {
     self.parse_signed(int64Type, "", p.vi())                                 // PARSE     int64
     self.match_char('"')
-    if vt := p.vt(); !mapfast(vt) {
+    if vt := p.vt(); !rt.IsMapfast(vt) {
         self.mapassign_std(vt, _VAR_st_Iv)              // MAPASSIGN int64, mapassign, st.Iv
     } else {
         self.Emit("MOVQ", _VAR_st_Iv, _AX)              // MOVQ      st.Iv, AX
@@ -1545,7 +1619,7 @@ func (self *_Assembler) _asm_OP_map_key_u32(p *_Instr) {
     self.parse_unsigned(uint32Type, "", p.vi())                                       // PARSE     uint32
     self.range_unsigned_CX(_I_uint32, _T_uint32, math.MaxUint32)   // RANGE     uint32
     self.match_char('"')
-    if vt := p.vt(); !mapfast(vt) {
+    if vt := p.vt(); !rt.IsMapfast(vt) {
         self.mapassign_std(vt, _VAR_st_Iv)                      // MAPASSIGN uint32, vt.Iv
     } else {
         self.Emit("MOVQ", _CX, _AX)                             // MOVQ CX, AX
@@ -1556,7 +1630,7 @@ func (self *_Assembler) _asm_OP_map_key_u32(p *_Instr) {
 func (self *_Assembler) _asm_OP_map_key_u64(p *_Instr) {
     self.parse_unsigned(uint64Type, "", p.vi())                                       // PARSE     uint64
     self.match_char('"')
-    if vt := p.vt(); !mapfast(vt) {
+    if vt := p.vt(); !rt.IsMapfast(vt) {
         self.mapassign_std(vt, _VAR_st_Iv)                      // MAPASSIGN uint64, vt.Iv
     } else {
         self.Emit("MOVQ", _VAR_st_Iv, _AX)                      // MOVQ      st.Iv, AX
@@ -1581,7 +1655,7 @@ func (self *_Assembler) _asm_OP_map_key_f64(p *_Instr) {
 func (self *_Assembler) _asm_OP_map_key_str(p *_Instr) {
     self.parse_string()                          // PARSE     STRING
     self.unquote_once(_ARG_sv_p, _ARG_sv_n, true, true)      // UNQUOTE   once, sv.p, sv.n
-    if vt := p.vt(); !mapfast(vt) {
+    if vt := p.vt(); !rt.IsMapfast(vt) {
         self.valloc(vt.Key(), _DI)
         self.Emit("MOVOU", _ARG_sv, _X0)
         self.Emit("MOVOU", _X0, jit.Ptr(_DI, 0))
@@ -1698,12 +1772,6 @@ func (self *_Assembler) _asm_OP_slice_append(p *_Instr) {
     self.Link("_append_slice_end_{n}")
 }
 
-func (self *_Assembler) _asm_OP_object_skip(_ *_Instr) {
-    self.call_sf(_F_skip_object)                // CALL_SF skip_object
-    self.Emit("TESTQ", _AX, _AX)                // TESTQ   AX, AX
-    self.Sjmp("JS"   , _LB_parsing_error_v)     // JS      _parse_error_v
-}
-
 func (self *_Assembler) _asm_OP_object_next(_ *_Instr) {
     self.call_sf(_F_skip_one)                   // CALL_SF skip_one
     self.Emit("TESTQ", _AX, _AX)                // TESTQ   AX, AX
@@ -1761,6 +1829,8 @@ func (self *_Assembler) _asm_OP_struct_field(p *_Instr) {
     self.Emit("MOVQ" , _R8, _VAR_sr)                            // MOVQ    R8, sr
     self.Sjmp("JMP"  , "_end_{n}")                              // JMP     _end_{n}
     self.Link("_try_lowercase_{n}")                             // _try_lowercase_{n}:
+    self.Emit("BTQ"  , jit.Imm(_F_case_sensitive), _ARG_fv)     // check if enable option CaseSensitive
+    self.Sjmp("JC"   , "_unknown_{n}")                         
     self.Emit("MOVQ" , jit.Imm(referenceFields(p.vf())), _AX)   // MOVQ    ${p.vf()}, AX
     self.Emit("MOVQ", _ARG_sv_p, _BX)                            // MOVQ   sv, BX
     self.Emit("MOVQ", _ARG_sv_n, _CX)                            // MOVQ   sv, CX
@@ -1768,17 +1838,29 @@ func (self *_Assembler) _asm_OP_struct_field(p *_Instr) {
     self.Emit("MOVQ" , _AX, _VAR_sr)                            // MOVQ    AX, _VAR_sr
     self.Emit("TESTQ", _AX, _AX)                                // TESTQ   AX, AX
     self.Sjmp("JNS"  , "_end_{n}")                              // JNS     _end_{n}
+    self.Link("_unknown_{n}")
+    // HACK: because `_VAR_sr` maybe used in `F_vstring`, so we should clear here again for `_OP_switch`.
+    self.Emit("MOVQ" , jit.Imm(-1), _AX)                        // MOVQ    $-1, AX
+    self.Emit("MOVQ" , _AX, _VAR_sr)                            // MOVQ    AX, sr
     self.Emit("BTQ"  , jit.Imm(_F_disable_unknown), _ARG_fv)    // BTQ     ${_F_disable_unknown}, fv
     self.Sjmp("JC"   , _LB_field_error)                         // JC      _field_error
     self.Link("_end_{n}")                                       // _end_{n}:
 }
 
 func (self *_Assembler) _asm_OP_unmarshal(p *_Instr) {
-    self.unmarshal_json(p.vt(), true)
+    if iv := p.i64(); iv != 0 {
+        self.unmarshal_json(p.vt(), true, _F_decodeJsonUnmarshalerQuoted)
+    } else {
+        self.unmarshal_json(p.vt(), true, _F_decodeJsonUnmarshaler)
+    }
 }
 
 func (self *_Assembler) _asm_OP_unmarshal_p(p *_Instr) {
-    self.unmarshal_json(p.vt(), false)
+    if iv := p.i64(); iv != 0 {
+        self.unmarshal_json(p.vt(), false, _F_decodeJsonUnmarshalerQuoted)
+    } else {
+        self.unmarshal_json(p.vt(), false, _F_decodeJsonUnmarshaler)
+    }
 }
 
 func (self *_Assembler) _asm_OP_unmarshal_text(p *_Instr) {

+ 103 - 32
vendor/github.com/bytedance/sonic/internal/decoder/compiler.go → vendor/github.com/bytedance/sonic/internal/decoder/jitdec/compiler.go

@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package decoder
+package jitdec
 
 import (
     `encoding/json`
@@ -54,6 +54,7 @@ const (
     _OP_nil_1
     _OP_nil_2
     _OP_nil_3
+    _OP_empty_bytes
     _OP_deref
     _OP_index
     _OP_is_null
@@ -77,7 +78,6 @@ const (
     _OP_array_clear_p
     _OP_slice_init
     _OP_slice_append
-    _OP_object_skip
     _OP_object_next
     _OP_struct_field
     _OP_unmarshal
@@ -97,8 +97,10 @@ const (
     _OP_check_char_0
     _OP_dismatch_err
     _OP_go_skip
+    _OP_skip_emtpy
     _OP_add
     _OP_check_empty
+    _OP_unsupported
     _OP_debug
 )
 
@@ -134,6 +136,7 @@ var _OpNames = [256]string {
     _OP_nil_1            : "nil_1",
     _OP_nil_2            : "nil_2",
     _OP_nil_3            : "nil_3",
+    _OP_empty_bytes      : "empty bytes",
     _OP_deref            : "deref",
     _OP_index            : "index",
     _OP_is_null          : "is_null",
@@ -155,7 +158,6 @@ var _OpNames = [256]string {
     _OP_array_skip       : "array_skip",
     _OP_slice_init       : "slice_init",
     _OP_slice_append     : "slice_append",
-    _OP_object_skip      : "object_skip",
     _OP_object_next      : "object_next",
     _OP_struct_field     : "struct_field",
     _OP_unmarshal        : "unmarshal",
@@ -177,6 +179,7 @@ var _OpNames = [256]string {
     _OP_add              : "add",
     _OP_go_skip          : "go_skip",
     _OP_check_empty      : "check_empty",
+    _OP_unsupported      : "unsupported type",
     _OP_debug            : "debug",
 }
 
@@ -271,6 +274,13 @@ func newInsVt(op _Op, vt reflect.Type) _Instr {
     }
 }
 
+func newInsVtI(op _Op, vt reflect.Type, iv int) _Instr {
+    return _Instr {
+        u: packOp(op) | rt.PackInt(iv),
+        p: unsafe.Pointer(rt.UnpackType(vt)),
+    }
+}
+
 func newInsVf(op _Op, vf *caching.FieldMap) _Instr {
     return _Instr {
         u: packOp(op),
@@ -452,6 +462,10 @@ func (self *_Program) rtt(op _Op, vt reflect.Type) {
     *self = append(*self, newInsVt(op, vt))
 }
 
+func (self *_Program) rtti(op _Op, vt reflect.Type, iv int) {
+    *self = append(*self, newInsVtI(op, vt, iv))
+}
+
 func (self *_Program) fmv(op _Op, vf *caching.FieldMap) {
     *self = append(*self, newInsVf(op, vf))
 }
@@ -527,35 +541,54 @@ func (self *_Compiler) compile(vt reflect.Type) (ret _Program, err error) {
     return
 }
 
-func (self *_Compiler) checkMarshaler(p *_Program, vt reflect.Type) bool {
+const (
+    checkMarshalerFlags_quoted = 1
+)
+
+func (self *_Compiler) checkMarshaler(p *_Program, vt reflect.Type, flags int, exec bool) bool {
     pt := reflect.PtrTo(vt)
 
     /* check for `json.Unmarshaler` with pointer receiver */
     if pt.Implements(jsonUnmarshalerType) {
-        p.rtt(_OP_unmarshal_p, pt)
+        if exec {
+            p.add(_OP_lspace)
+            p.rtti(_OP_unmarshal_p, pt, flags)
+        }
         return true
     }
 
     /* check for `json.Unmarshaler` */
     if vt.Implements(jsonUnmarshalerType) {
-        p.add(_OP_lspace)
-        self.compileUnmarshalJson(p, vt)
+        if exec {
+            p.add(_OP_lspace)
+            self.compileUnmarshalJson(p, vt, flags)
+        }
         return true
     }
 
+    if flags == checkMarshalerFlags_quoted {
+        // text marshaler shouldn't be supported for quoted string
+        return false
+    }
+
     /* check for `encoding.TextMarshaler` with pointer receiver */
     if pt.Implements(encodingTextUnmarshalerType) {
-        p.add(_OP_lspace)
-        self.compileUnmarshalTextPtr(p, pt)
+        if exec {
+            p.add(_OP_lspace)
+            self.compileUnmarshalTextPtr(p, pt, flags)
+        }
         return true
     }
 
     /* check for `encoding.TextUnmarshaler` */
     if vt.Implements(encodingTextUnmarshalerType) {
-        p.add(_OP_lspace)
-        self.compileUnmarshalText(p, vt)
+        if exec {
+            p.add(_OP_lspace)
+            self.compileUnmarshalText(p, vt, flags)
+        }
         return true
     }
+
     return false
 }
 
@@ -567,7 +600,7 @@ func (self *_Compiler) compileOne(p *_Program, sp int, vt reflect.Type) {
         return
     }
 
-    if self.checkMarshaler(p, vt) {
+    if self.checkMarshaler(p, vt, 0, true) {
         return
     }
 
@@ -601,10 +634,17 @@ func (self *_Compiler) compileOps(p *_Program, sp int, vt reflect.Type) {
         case reflect.Ptr       : self.compilePtr       (p, sp, vt)
         case reflect.Slice     : self.compileSlice     (p, sp, vt)
         case reflect.Struct    : self.compileStruct    (p, sp, vt)
-        default                : panic                 (&json.UnmarshalTypeError{Type: vt})
+        default                : self.compileUnsupportedType      (p, vt)
     }
 }
 
+func (self *_Compiler) compileUnsupportedType(p *_Program, vt reflect.Type) {
+    i := p.pc()
+    p.add(_OP_is_null)
+    p.rtt(_OP_unsupported, vt)
+    p.pin(i)
+}
+
 func (self *_Compiler) compileMap(p *_Program, sp int, vt reflect.Type) {
     if reflect.PtrTo(vt.Key()).Implements(encodingTextUnmarshalerType) {
         self.compileMapOp(p, sp, vt, _OP_map_key_utext_p)
@@ -690,7 +730,7 @@ func (self *_Compiler) compilePtr(p *_Program, sp int, et reflect.Type) {
 
     /* dereference all the way down */
     for et.Kind() == reflect.Ptr {
-        if self.checkMarshaler(p, et) {
+        if self.checkMarshaler(p, et, 0, true) {
             return
         }
         et = et.Elem()
@@ -707,7 +747,7 @@ func (self *_Compiler) compilePtr(p *_Program, sp int, et reflect.Type) {
         self.tab[et] = true
 
         /* not inline the pointer type
-        * recursing the defined pointer type's elem will casue issue379.
+        * recursing the defined pointer type's elem will cause issue379.
         */
         self.compileOps(p, sp, et)
     }
@@ -790,12 +830,19 @@ func (self *_Compiler) compileSliceBin(p *_Program, sp int, vt reflect.Type) {
     self.compileSliceBody(p, sp, vt.Elem())
     y := p.pc()
     p.add(_OP_goto)
+
+    // unmarshal `null` and `"` is different
     p.pin(i)
-    p.pin(k)
     p.add(_OP_nil_3)
+    y2 := p.pc()
+    p.add(_OP_goto)
+
+    p.pin(k)
+    p.add(_OP_empty_bytes)
     p.pin(x)
     p.pin(skip)
     p.pin(y)
+    p.pin(y2)
 }
 
 func (self *_Compiler) compileSliceList(p *_Program, sp int, vt reflect.Type) {
@@ -872,7 +919,24 @@ func (self *_Compiler) compileStructBody(p *_Program, sp int, vt reflect.Type) {
     n := p.pc()
     p.add(_OP_is_null)
 
-    skip := self.checkIfSkip(p, vt, '{')
+    j := p.pc()
+    p.chr(_OP_check_char_0, '{')
+    p.rtt(_OP_dismatch_err, vt)
+
+    /* special case for empty object */
+    if len(fv) == 0 {
+        p.pin(j)
+        s := p.pc()
+        p.add(_OP_skip_emtpy)
+        p.pin(s)
+        p.pin(n)
+        return
+    }
+
+    skip := p.pc()
+    p.add(_OP_go_skip)
+    p.pin(j)
+    p.int(_OP_add, 1)
     
     p.add(_OP_save)
     p.add(_OP_lspace)
@@ -890,11 +954,6 @@ func (self *_Compiler) compileStructBody(p *_Program, sp int, vt reflect.Type) {
     p.chr(_OP_check_char, '}')
     p.chr(_OP_match_char, ',')
 
-    /* special case of an empty struct */
-    if len(fv) == 0 {
-        p.add(_OP_object_skip)
-        goto end_of_object
-    }
 
     /* match the remaining fields */
     p.add(_OP_lspace)
@@ -930,7 +989,6 @@ func (self *_Compiler) compileStructBody(p *_Program, sp int, vt reflect.Type) {
         p.int(_OP_goto, y0)
     }
 
-end_of_object:
     p.pin(x)
     p.pin(y1)
     p.add(_OP_drop)
@@ -938,7 +996,22 @@ end_of_object:
     p.pin(skip)
 }
 
+func (self *_Compiler) compileStructFieldStrUnmarshal(p *_Program, vt reflect.Type) {
+    p.add(_OP_lspace)
+    n0 := p.pc()
+    p.add(_OP_is_null)
+    self.checkMarshaler(p, vt, checkMarshalerFlags_quoted, true)
+    p.pin(n0)
+}
+
 func (self *_Compiler) compileStructFieldStr(p *_Program, sp int, vt reflect.Type) {
+    // according to std, json.Unmarshaler should be called before stringize
+    // see https://github.com/bytedance/sonic/issues/670
+    if self.checkMarshaler(p, vt, checkMarshalerFlags_quoted, false) {
+        self.compileStructFieldStrUnmarshal(p, vt)
+        return
+    }
+
     n1 := -1
     ft := vt
     sv := false
@@ -1080,13 +1153,11 @@ func (self *_Compiler) compileInterface(p *_Program, vt reflect.Type) {
     p.pin(j)
 }
 
-func (self *_Compiler) compilePrimitive(vt reflect.Type, p *_Program, op _Op) {
+func (self *_Compiler) compilePrimitive(_ reflect.Type, p *_Program, op _Op) {
     i := p.pc()
     p.add(_OP_is_null)
-    // skip := self.checkPrimitive(p, vt)
     p.add(op)
     p.pin(i)
-    // p.pin(skip)
 }
 
 func (self *_Compiler) compileUnmarshalEnd(p *_Program, vt reflect.Type, i int) {
@@ -1106,7 +1177,7 @@ func (self *_Compiler) compileUnmarshalEnd(p *_Program, vt reflect.Type, i int)
     p.pin(j)
 }
 
-func (self *_Compiler) compileUnmarshalJson(p *_Program, vt reflect.Type) {
+func (self *_Compiler) compileUnmarshalJson(p *_Program, vt reflect.Type, flags int) {
     i := p.pc()
     v := _OP_unmarshal
     p.add(_OP_is_null)
@@ -1117,11 +1188,11 @@ func (self *_Compiler) compileUnmarshalJson(p *_Program, vt reflect.Type) {
     }
 
     /* call the unmarshaler */
-    p.rtt(v, vt)
+    p.rtti(v, vt, flags)
     self.compileUnmarshalEnd(p, vt, i)
 }
 
-func (self *_Compiler) compileUnmarshalText(p *_Program, vt reflect.Type) {
+func (self *_Compiler) compileUnmarshalText(p *_Program, vt reflect.Type, iv int) {
     i := p.pc()
     v := _OP_unmarshal_text
     p.add(_OP_is_null)
@@ -1134,15 +1205,15 @@ func (self *_Compiler) compileUnmarshalText(p *_Program, vt reflect.Type) {
     }
 
     /* call the unmarshaler */
-    p.rtt(v, vt)
+    p.rtti(v, vt, iv)
     self.compileUnmarshalEnd(p, vt, i)
 }
 
-func (self *_Compiler) compileUnmarshalTextPtr(p *_Program, vt reflect.Type) {
+func (self *_Compiler) compileUnmarshalTextPtr(p *_Program, vt reflect.Type, iv int) {
     i := p.pc()
     p.add(_OP_is_null)
     p.chr(_OP_match_char, '"')
-    p.rtt(_OP_unmarshal_text_p, vt)
+    p.rtti(_OP_unmarshal_text_p, vt, iv)
     p.pin(i)
 }
 

+ 1 - 1
vendor/github.com/bytedance/sonic/internal/decoder/debug.go → vendor/github.com/bytedance/sonic/internal/decoder/jitdec/debug.go

@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package decoder
+package jitdec
 
 import (
 	`os`

+ 141 - 0
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/decoder.go

@@ -0,0 +1,141 @@
+package jitdec
+
+import (
+    `unsafe`
+    `encoding/json`
+    `reflect`
+    `runtime`
+
+	`github.com/bytedance/sonic/internal/decoder/consts`
+	`github.com/bytedance/sonic/internal/decoder/errors`
+    `github.com/bytedance/sonic/internal/rt`
+    `github.com/bytedance/sonic/utf8`
+	`github.com/bytedance/sonic/option`
+)
+
+type (
+	MismatchTypeError = errors.MismatchTypeError
+	SyntaxError = errors.SyntaxError
+)
+
+const (
+	_F_allow_control = consts.F_allow_control
+	_F_copy_string = consts.F_copy_string
+	_F_disable_unknown = consts.F_disable_unknown
+	_F_disable_urc = consts.F_disable_urc
+	_F_use_int64 = consts.F_use_int64
+	_F_use_number = consts.F_use_number
+	_F_no_validate_json = consts.F_no_validate_json
+	_F_validate_string = consts.F_validate_string
+    _F_case_sensitive = consts.F_case_sensitive
+)
+
+var (
+	error_wrap = errors.ErrorWrap
+	error_type = errors.ErrorType
+	error_field = errors.ErrorField
+	error_value = errors.ErrorValue
+	error_mismatch = errors.ErrorMismatch
+	stackOverflow = errors.StackOverflow
+)
+
+
+// Decode parses the JSON-encoded data from current position and stores the result
+// in the value pointed to by val.
+func Decode(s *string, i *int, f uint64, val interface{}) error {
+    /* validate json if needed */
+    if (f & (1 << _F_validate_string)) != 0  && !utf8.ValidateString(*s){
+        dbuf := utf8.CorrectWith(nil, rt.Str2Mem(*s), "\ufffd")
+        *s = rt.Mem2Str(dbuf)
+    }
+
+    vv := rt.UnpackEface(val)
+    vp := vv.Value
+
+    /* check for nil type */
+    if vv.Type == nil {
+        return &json.InvalidUnmarshalError{}
+    }
+
+    /* must be a non-nil pointer */
+    if vp == nil || vv.Type.Kind() != reflect.Ptr {
+        return &json.InvalidUnmarshalError{Type: vv.Type.Pack()}
+    }
+
+    etp := rt.PtrElem(vv.Type)
+
+    /* check the defined pointer type for issue 379 */
+    if vv.Type.IsNamed() {
+        newp := vp
+        etp  = vv.Type
+        vp   = unsafe.Pointer(&newp)
+    }
+
+    /* create a new stack, and call the decoder */
+    sb := newStack()
+    nb, err := decodeTypedPointer(*s, *i, etp, vp, sb, f)
+    /* return the stack back */
+    *i = nb
+    freeStack(sb)
+
+    /* avoid GC ahead */
+    runtime.KeepAlive(vv)
+    return err
+}
+
+
+// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
+// order to reduce the first-hit latency.
+//
+// Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is
+// a compile option to set the depth of recursive compile for the nested struct type.
+func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
+    cfg := option.DefaultCompileOptions()
+    for _, opt := range opts {
+        opt(&cfg)
+    }
+    return pretouchRec(map[reflect.Type]bool{vt:true}, cfg)
+}
+
+func pretouchType(_vt reflect.Type, opts option.CompileOptions) (map[reflect.Type]bool, error) {
+    /* compile function */
+    compiler := newCompiler().apply(opts)
+    decoder := func(vt *rt.GoType, _ ...interface{}) (interface{}, error) {
+        if pp, err := compiler.compile(_vt); err != nil {
+            return nil, err
+        } else {
+            as := newAssembler(pp)
+            as.name = _vt.String()
+            return as.Load(), nil
+        }
+    }
+
+    /* find or compile */
+    vt := rt.UnpackType(_vt)
+    if val := programCache.Get(vt); val != nil {
+        return nil, nil
+    } else if _, err := programCache.Compute(vt, decoder); err == nil {
+        return compiler.rec, nil
+    } else {
+        return nil, err
+    }
+}
+
+func pretouchRec(vtm map[reflect.Type]bool, opts option.CompileOptions) error {
+    if opts.RecursiveDepth < 0 || len(vtm) == 0 {
+        return nil
+    }
+    next := make(map[reflect.Type]bool)
+    for vt := range(vtm) {
+        sub, err := pretouchType(vt, opts)
+        if err != nil {
+            return err
+        }
+        for svt := range(sub) {
+            next[svt] = true
+        }
+    }
+    opts.RecursiveDepth -= 1
+    return pretouchRec(next, opts)
+}
+

+ 6 - 5
vendor/github.com/bytedance/sonic/internal/decoder/generic_regabi_amd64.go → vendor/github.com/bytedance/sonic/internal/decoder/jitdec/generic_regabi_amd64.go

@@ -1,4 +1,4 @@
-// +build go1.17,!go1.23
+// +build go1.17,!go1.25
 
 /*
  * Copyright 2021 ByteDance Inc.
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-package decoder
+package jitdec
 
 import (
     `encoding/json`
@@ -27,6 +27,7 @@ import (
     `github.com/bytedance/sonic/internal/native`
     `github.com/bytedance/sonic/internal/native/types`
     `github.com/twitchyliquid64/golang-asm/obj`
+    `github.com/bytedance/sonic/internal/rt`
 )
 
 /** Crucial Registers:
@@ -173,8 +174,8 @@ var (
 )
 
 var (
-    _F_convTslice    = jit.Func(convTslice)
-    _F_convTstring   = jit.Func(convTstring)
+    _F_convTslice    = jit.Func(rt.ConvTslice)
+    _F_convTstring   = jit.Func(rt.ConvTstring)
     _F_invalid_vtype = jit.Func(invalid_vtype)
 )
 
@@ -725,5 +726,5 @@ var (
 
 //go:nosplit
 func invalid_vtype(vt types.ValueType) {
-    throw(fmt.Sprintf("invalid value type: %d", vt))
+    rt.Throw(fmt.Sprintf("invalid value type: %d", vt))
 }

+ 1 - 1
vendor/github.com/bytedance/sonic/internal/decoder/generic_regabi_amd64_test.s → vendor/github.com/bytedance/sonic/internal/decoder/jitdec/generic_regabi_amd64_test.s

@@ -1,4 +1,4 @@
-// +build go1.17,!go1.23
+// +build go1.17,!go1.25
 
 //
 // Copyright 2021 ByteDance Inc.

+ 6 - 4
vendor/github.com/bytedance/sonic/internal/decoder/pools.go → vendor/github.com/bytedance/sonic/internal/decoder/jitdec/pools.go

@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package decoder
+package jitdec
 
 import (
     `sync`
@@ -36,6 +36,7 @@ const (
     _PtrBytes   = _PTR_SIZE / 8
     _FsmOffset  = (_MaxStack + 1) * _PtrBytes
     _DbufOffset = _FsmOffset + int64(unsafe.Sizeof(types.StateMachine{})) + types.MAX_RECURSE * _PtrBytes
+    _EpOffset   = _DbufOffset + _MaxDigitNums
     _StackSize  = unsafe.Sizeof(_Stack{})
 )
 
@@ -53,6 +54,7 @@ type _Stack struct {
     mm types.StateMachine
     vp [types.MAX_RECURSE]unsafe.Pointer
     dp [_MaxDigitNums]byte
+    ep unsafe.Pointer
 }
 
 type _Decoder func(
@@ -61,8 +63,8 @@ type _Decoder func(
     vp unsafe.Pointer,
     sb *_Stack,
     fv uint64,
-    sv string, // DO NOT pass value to this arguement, since it is only used for local _VAR_sv
-    vk unsafe.Pointer, // DO NOT pass value to this arguement, since it is only used for local _VAR_vk
+    sv string, // DO NOT pass value to this argument, since it is only used for local _VAR_sv
+    vk unsafe.Pointer, // DO NOT pass value to this argument, since it is only used for local _VAR_vk
 ) (int, error)
 
 var _KeepAlive struct {
@@ -100,7 +102,7 @@ func newStack() *_Stack {
 }
 
 func resetStack(p *_Stack) {
-    memclrNoHeapPointers(unsafe.Pointer(p), _StackSize)
+    rt.MemclrNoHeapPointers(unsafe.Pointer(p), _StackSize)
 }
 
 func freeStack(p *_Stack) {

+ 15 - 1
vendor/github.com/bytedance/sonic/internal/decoder/primitives.go → vendor/github.com/bytedance/sonic/internal/decoder/jitdec/primitives.go

@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package decoder
+package jitdec
 
 import (
     `encoding`
@@ -39,6 +39,20 @@ func decodeJsonUnmarshaler(vv interface{}, s string) error {
     return vv.(json.Unmarshaler).UnmarshalJSON(rt.Str2Mem(s))
 }
 
+// used to distinguish between MismatchQuoted and other MismatchedTyped errors, see issue #670 and #716
+type MismatchQuotedError struct {}
+
+func (*MismatchQuotedError) Error() string {
+    return "mismatch quoted"
+}
+
+func decodeJsonUnmarshalerQuoted(vv interface{}, s string) error {
+    if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
+        return &MismatchQuotedError{}
+    }
+    return vv.(json.Unmarshaler).UnmarshalJSON(rt.Str2Mem(s[1:len(s)-1]))
+}
+
 func decodeTextUnmarshaler(vv interface{}, s string) error {
     return vv.(encoding.TextUnmarshaler).UnmarshalText(rt.Str2Mem(s))
 }

+ 1 - 1
vendor/github.com/bytedance/sonic/internal/decoder/types.go → vendor/github.com/bytedance/sonic/internal/decoder/jitdec/types.go

@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package decoder
+package jitdec
 
 import (
     `encoding`

+ 1 - 1
vendor/github.com/bytedance/sonic/internal/decoder/utils.go → vendor/github.com/bytedance/sonic/internal/decoder/jitdec/utils.go

@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package decoder
+package jitdec
 
 import (
     `unsafe`

+ 174 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/compile_struct.go

@@ -0,0 +1,174 @@
+package optdec
+
+import (
+	"fmt"
+	"reflect"
+
+	caching "github.com/bytedance/sonic/internal/optcaching"
+	"github.com/bytedance/sonic/internal/rt"
+	"github.com/bytedance/sonic/internal/resolver"
+)
+
+const (
+    _MAX_FIELDS = 50        // cutoff at 50 fields struct
+)
+
+func (c *compiler) compileIntStringOption(vt reflect.Type) decFunc {
+	switch vt.Size() {
+	case 4:
+		switch vt.Kind() {
+		case reflect.Uint:
+			fallthrough
+		case reflect.Uintptr:
+			return &u32StringDecoder{}
+		case reflect.Int:
+			return &i32StringDecoder{}
+		}
+	case 8:
+		switch vt.Kind() {
+		case reflect.Uint:
+			fallthrough
+		case reflect.Uintptr:
+			return &u64StringDecoder{}
+		case reflect.Int:
+			return &i64StringDecoder{}
+		}
+	default:
+		panic("not supported pointer size: " + fmt.Sprint(vt.Size()))
+	}
+	panic("unreachable")
+}
+
+func isInteger(vt reflect.Type) bool {
+	switch vt.Kind() {
+		case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr, reflect.Int: return true
+		default: return false
+	}
+}
+
+func (c *compiler) assertStringOptTypes(vt reflect.Type) {
+	if c.depth > _CompileMaxDepth {
+		panic(*stackOverflow)
+	}
+
+	c.depth += 1
+	defer func ()  {
+		c.depth -= 1
+	}()
+
+	if isInteger(vt) {
+		return
+	}
+
+	switch vt.Kind() {
+	case reflect.String, reflect.Bool, reflect.Float32, reflect.Float64:
+		return
+	case reflect.Ptr: c.assertStringOptTypes(vt.Elem())
+	default:
+		panicForInvalidStrType(vt)
+	}
+}
+
+func (c *compiler) compileFieldStringOption(vt reflect.Type) decFunc {
+	c.assertStringOptTypes(vt)
+	unmDec := c.tryCompilePtrUnmarshaler(vt, true)
+	if unmDec != nil { 
+		return unmDec
+	} 
+
+	switch vt.Kind() {
+	case reflect.String:
+		if vt == jsonNumberType {
+			return &numberStringDecoder{}
+		}
+		return &strStringDecoder{}
+	case reflect.Bool:
+		return &boolStringDecoder{}
+	case reflect.Int8:
+		return &i8StringDecoder{}
+	case reflect.Int16:
+		return &i16StringDecoder{}
+	case reflect.Int32:
+		return &i32StringDecoder{}
+	case reflect.Int64:
+		return &i64StringDecoder{}
+	case reflect.Uint8:
+		return &u8StringDecoder{}
+	case reflect.Uint16:
+		return &u16StringDecoder{}
+	case reflect.Uint32:
+		return &u32StringDecoder{}
+	case reflect.Uint64:
+		return &u64StringDecoder{}
+	case reflect.Float32:
+		return &f32StringDecoder{}
+	case reflect.Float64:
+		return &f64StringDecoder{}
+	case reflect.Uint:
+		fallthrough
+	case reflect.Uintptr:
+		fallthrough
+	case reflect.Int:
+		return c.compileIntStringOption(vt)
+	case reflect.Ptr:
+		return &ptrStrDecoder{
+			typ:   rt.UnpackType(vt.Elem()),
+			deref: c.compileFieldStringOption(vt.Elem()),
+		}
+	default:
+		panicForInvalidStrType(vt)
+		return nil
+	}
+}
+
+func (c *compiler) compileStruct(vt reflect.Type) decFunc {
+	c.enter(vt)
+	defer c.exit(vt)
+	if c.namedPtr {
+		c.namedPtr = false
+		return c.compileStructBody(vt)
+	}
+
+	if c.depth >= c.opts.MaxInlineDepth + 1 || (c.counts > 0 &&  vt.NumField() >= _MAX_FIELDS) {
+		return &recuriveDecoder{
+			typ: rt.UnpackType(vt),
+		}
+	} else {
+		return c.compileStructBody(vt)
+	}
+}
+
+func (c *compiler) compileStructBody(vt reflect.Type) decFunc {
+	fv := resolver.ResolveStruct(vt)
+	entries := make([]fieldEntry, 0, len(fv))
+
+	for _, f := range fv {
+		var dec decFunc
+		/* dealt with field tag options */
+		if f.Opts&resolver.F_stringize != 0 {
+			dec = c.compileFieldStringOption(f.Type)
+		} else {
+			dec = c.compile(f.Type)
+		}
+
+		/* deal with embedded pointer fields */
+		if f.Path[0].Kind == resolver.F_deref {
+			dec = &embeddedFieldPtrDecoder{
+				field:    	f,
+				fieldDec:   dec,
+				fieldName:  f.Name,
+			}
+		}
+
+		entries = append(entries, fieldEntry{
+			FieldMeta: f,
+			fieldDec:  dec,
+		})
+	}
+	return &structDecoder{
+		fieldMap:  	caching.NewFieldLookup(fv),
+		fields:     entries,
+		structName: vt.Name(),
+		typ: 		vt,
+	}
+}

+ 460 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/compiler.go

@@ -0,0 +1,460 @@
+package optdec
+
+import (
+	"fmt"
+	"reflect"
+
+	"github.com/bytedance/sonic/option"
+	"github.com/bytedance/sonic/internal/rt"
+	"github.com/bytedance/sonic/internal/caching"
+)
+
+var (
+	programCache = caching.CreateProgramCache()
+)
+
+func findOrCompile(vt *rt.GoType) (decFunc, error) {
+	makeDecoder := func(vt *rt.GoType, _ ...interface{}) (interface{}, error) {
+		ret, err := newCompiler().compileType(vt.Pack())
+		return ret, err
+	}
+	if val := programCache.Get(vt); val != nil {
+		return val.(decFunc), nil
+	} else if ret, err := programCache.Compute(vt, makeDecoder); err == nil {
+		return ret.(decFunc), nil
+	} else {
+		return nil, err
+	}
+}
+
+type compiler struct {
+	visited map[reflect.Type]bool
+	depth   int
+	counts  int
+	opts 	option.CompileOptions
+	namedPtr bool
+}
+
+func newCompiler() *compiler {
+	return &compiler{
+		visited: make(map[reflect.Type]bool),
+		opts:  option.DefaultCompileOptions(),
+	}
+}
+
+func (self *compiler) apply(opts option.CompileOptions) *compiler {
+	self.opts = opts
+	return self
+}
+
+const _CompileMaxDepth = 4096
+
+func (c *compiler) enter(vt reflect.Type) {
+	c.visited[vt] = true
+	c.depth += 1
+
+	if c.depth > _CompileMaxDepth {
+		panic(*stackOverflow)
+	}
+}
+
+func (c *compiler) exit(vt reflect.Type) {
+	c.visited[vt] = false
+	c.depth -= 1
+}
+
+func (c *compiler) compileInt(vt reflect.Type) decFunc {
+	switch vt.Size() {
+	case 4:
+		switch vt.Kind() {
+		case reflect.Uint:
+			fallthrough
+		case reflect.Uintptr:
+			return &u32Decoder{}
+		case reflect.Int:
+			return &i32Decoder{}
+		}
+	case 8:
+		switch vt.Kind() {
+		case reflect.Uint:
+			fallthrough
+		case reflect.Uintptr:
+			return &u64Decoder{}
+		case reflect.Int:
+			return &i64Decoder{}
+		}
+	default:
+		panic("not supported pointer size: " + fmt.Sprint(vt.Size()))
+	}
+	panic("unreachable")
+}
+
+func (c *compiler) rescue(ep *error) {
+	if val := recover(); val != nil {
+		if err, ok := val.(error); ok {
+			*ep = err
+		} else {
+			panic(val)
+		}
+	}
+}
+
+func (c *compiler) compileType(vt reflect.Type) (rt decFunc, err error) {
+	defer c.rescue(&err)
+	rt = c.compile(vt)
+	return rt, err
+}
+
+func (c *compiler) compile(vt reflect.Type) decFunc {
+	if c.visited[vt] {
+		return &recuriveDecoder{
+			typ: rt.UnpackType(vt),
+		}
+	}
+
+	dec := c.tryCompilePtrUnmarshaler(vt, false)
+	if dec != nil {
+		return dec
+	}
+
+	return c.compileBasic(vt)
+}
+
+func (c *compiler) compileBasic(vt reflect.Type) decFunc {
+	defer func() {
+		c.counts += 1
+	}()
+	switch vt.Kind() {
+	case reflect.Bool:
+		return &boolDecoder{}
+	case reflect.Int8:
+		return &i8Decoder{}
+	case reflect.Int16:
+		return &i16Decoder{}
+	case reflect.Int32:
+		return &i32Decoder{}
+	case reflect.Int64:
+		return &i64Decoder{}
+	case reflect.Uint8:
+		return &u8Decoder{}
+	case reflect.Uint16:
+		return &u16Decoder{}
+	case reflect.Uint32:
+		return &u32Decoder{}
+	case reflect.Uint64:
+		return &u64Decoder{}
+	case reflect.Float32:
+		return &f32Decoder{}
+	case reflect.Float64:
+		return &f64Decoder{}
+	case reflect.Uint:
+		fallthrough
+	case reflect.Uintptr:
+		fallthrough
+	case reflect.Int:
+		return c.compileInt(vt)
+	case reflect.String:
+		return c.compileString(vt)
+	case reflect.Array:
+		return c.compileArray(vt)
+	case reflect.Interface:
+		return c.compileInterface(vt)
+	case reflect.Map:
+		return c.compileMap(vt)
+	case reflect.Ptr:
+		return c.compilePtr(vt)
+	case reflect.Slice:
+		return c.compileSlice(vt)
+	case reflect.Struct:
+		return c.compileStruct(vt)
+	default:
+		return &unsupportedTypeDecoder{
+			typ: rt.UnpackType(vt),
+		}
+	}
+}
+
+func (c *compiler) compilePtr(vt reflect.Type) decFunc {
+	c.enter(vt)
+	defer c.exit(vt)
+
+	// special logic for Named Ptr, issue 379
+	if reflect.PtrTo(vt.Elem()) != vt {
+		c.namedPtr = true
+		return &ptrDecoder{
+			typ:   rt.UnpackType(vt.Elem()),
+			deref: c.compileBasic(vt.Elem()),
+		}
+	}
+
+	return &ptrDecoder{
+		typ:   rt.UnpackType(vt.Elem()),
+		deref: c.compile(vt.Elem()),
+	}
+}
+
+func (c *compiler) compileArray(vt reflect.Type) decFunc {
+	c.enter(vt)
+	defer c.exit(vt)
+	return &arrayDecoder{
+		len:      vt.Len(),
+		elemType: rt.UnpackType(vt.Elem()),
+		elemDec:  c.compile(vt.Elem()),
+		typ: vt,
+	}
+}
+
+func (c *compiler) compileString(vt reflect.Type) decFunc {
+	if vt == jsonNumberType {
+		return &numberDecoder{}
+	}
+	return &stringDecoder{}
+
+}
+
+func (c *compiler) tryCompileSliceUnmarshaler(vt reflect.Type) decFunc {
+	pt := reflect.PtrTo(vt.Elem())
+	if pt.Implements(jsonUnmarshalerType) {
+		return &sliceDecoder{
+			elemType: rt.UnpackType(vt.Elem()),
+			elemDec:  c.compile(vt.Elem()),
+			typ: vt,
+		}
+	}
+
+	if pt.Implements(encodingTextUnmarshalerType) {
+		return &sliceDecoder{
+			elemType: rt.UnpackType(vt.Elem()),
+			elemDec:  c.compile(vt.Elem()),
+			typ: vt,
+		}
+	}
+	return nil
+}
+
+func (c *compiler) compileSlice(vt reflect.Type) decFunc {
+	c.enter(vt)
+	defer c.exit(vt)
+
+	// Some common slice, use a decoder, to avoid function calls
+	et := rt.UnpackType(vt.Elem())
+
+	/* first checking `[]byte` */
+	if et.Kind() == reflect.Uint8 /* []byte */ {
+		return c.compileSliceBytes(vt)
+	}
+
+	dec := c.tryCompileSliceUnmarshaler(vt)
+	if dec != nil {
+		return dec
+	}
+
+	if vt == reflect.TypeOf([]interface{}{}) {
+		return &sliceEfaceDecoder{}
+	}
+	if et.IsInt32() {
+		return &sliceI32Decoder{}
+	}
+	if et.IsInt64() {
+		return &sliceI64Decoder{}
+	}
+	if et.IsUint32() {
+		return &sliceU32Decoder{}
+	}
+	if et.IsUint64() {
+		return &sliceU64Decoder{}
+	}
+	if et.Kind() == reflect.String && et != rt.JsonNumberType {
+		return &sliceStringDecoder{}
+	}
+
+	return &sliceDecoder{
+		elemType: rt.UnpackType(vt.Elem()),
+		elemDec:  c.compile(vt.Elem()),
+		typ: vt,
+	}
+}
+
+func (c *compiler) compileSliceBytes(vt reflect.Type) decFunc {
+	ep := reflect.PtrTo(vt.Elem())
+
+	if ep.Implements(jsonUnmarshalerType) {
+		return &sliceBytesUnmarshalerDecoder{
+			elemType: rt.UnpackType(vt.Elem()),
+			elemDec:  c.compile(vt.Elem()),
+			typ: vt,
+		}
+	}
+
+	if ep.Implements(encodingTextUnmarshalerType) {
+		return &sliceBytesUnmarshalerDecoder{
+			elemType: rt.UnpackType(vt.Elem()),
+			elemDec:  c.compile(vt.Elem()),
+				typ: vt,
+		}
+	}
+
+	return &sliceBytesDecoder{}
+}
+
+func (c *compiler) compileInterface(vt reflect.Type) decFunc {
+	c.enter(vt)
+	defer c.exit(vt)
+	if vt.NumMethod() == 0 {
+		return &efaceDecoder{}
+	}
+
+	if vt.Implements(jsonUnmarshalerType) {
+		return &unmarshalJSONDecoder{
+			typ: rt.UnpackType(vt),
+		}
+	}
+
+	if vt.Implements(encodingTextUnmarshalerType) {
+		return &unmarshalTextDecoder{
+			typ: rt.UnpackType(vt),
+		}
+	}
+
+	return &ifaceDecoder{
+		typ: rt.UnpackType(vt),
+	}
+}
+
+func (c *compiler) compileMap(vt reflect.Type) decFunc {
+	c.enter(vt)
+	defer c.exit(vt)
+	// check the key unmarshaler at first
+	decKey := tryCompileKeyUnmarshaler(vt)
+	if decKey != nil {
+		return &mapDecoder{
+			mapType: rt.MapType(rt.UnpackType(vt)),
+			keyDec:  decKey,
+			elemDec: c.compile(vt.Elem()),
+		}
+	}
+
+	// Most common map, use a decoder, to avoid function calls
+	if vt == reflect.TypeOf(map[string]interface{}{}) {
+		return &mapEfaceDecoder{}
+	} else if vt == reflect.TypeOf(map[string]string{}) {
+		return &mapStringDecoder{}
+	}
+
+	// Some common integer map later
+	mt := rt.MapType(rt.UnpackType(vt))
+
+	if mt.Key.Kind() == reflect.String && mt.Key != rt.JsonNumberType {
+		return &mapStrKeyDecoder{
+			mapType: mt,
+			assign: rt.GetMapStrAssign(vt),
+			elemDec: c.compile(vt.Elem()),
+		}
+	}
+
+	if mt.Key.IsInt64() {
+		return &mapI64KeyDecoder{
+			mapType: mt,
+			elemDec: c.compile(vt.Elem()),
+			assign: rt.GetMap64Assign(vt),
+		}
+	}
+
+	if mt.Key.IsInt32() {
+		return &mapI32KeyDecoder{
+			mapType: mt,
+			elemDec: c.compile(vt.Elem()),
+			assign: rt.GetMap32Assign(vt),
+		}
+	}
+
+	if mt.Key.IsUint64() {
+		return &mapU64KeyDecoder{
+			mapType: mt,
+			elemDec: c.compile(vt.Elem()),
+			assign: rt.GetMap64Assign(vt),
+		}
+	}
+
+	if mt.Key.IsUint32() {
+		return &mapU32KeyDecoder{
+			mapType: mt,
+			elemDec: c.compile(vt.Elem()),
+			assign: rt.GetMap32Assign(vt),
+		}
+	}
+
+	// Generic map
+	return &mapDecoder{
+		mapType: mt,
+		keyDec:  c.compileMapKey(vt),
+		elemDec: c.compile(vt.Elem()),
+	}
+}
+
+func tryCompileKeyUnmarshaler(vt reflect.Type) decKey {
+	pt := reflect.PtrTo(vt.Key())
+
+	/* check for `encoding.TextUnmarshaler` with pointer receiver */
+	if pt.Implements(encodingTextUnmarshalerType) {
+		return decodeKeyTextUnmarshaler
+	}
+
+	/* NOTE: encoding/json not support map key with `json.Unmarshaler` */
+	return nil
+}
+
+func (c *compiler) compileMapKey(vt reflect.Type) decKey {
+	switch vt.Key().Kind() {
+	case reflect.Int8:
+		return decodeKeyI8
+	case reflect.Int16:
+		return decodeKeyI16
+	case reflect.Uint8:
+		return decodeKeyU8
+	case reflect.Uint16:
+		return decodeKeyU16
+	// NOTE: actually, encoding/json can't use float as map key
+	case reflect.Float32:
+		return decodeFloat32Key
+	case reflect.Float64:
+		return decodeFloat64Key
+	case reflect.String:
+		if rt.UnpackType(vt.Key()) == rt.JsonNumberType {
+			return decodeJsonNumberKey
+		}
+		fallthrough
+	default:
+		return nil
+	}
+}
+
+// maybe vt is a named type, and not a pointer receiver, see issue 379  
+func (c *compiler) tryCompilePtrUnmarshaler(vt reflect.Type, strOpt bool) decFunc {
+	pt := reflect.PtrTo(vt)
+
+	/* check for `json.Unmarshaler` with pointer receiver */
+	if pt.Implements(jsonUnmarshalerType) {
+		return &unmarshalJSONDecoder{
+			typ: rt.UnpackType(pt),
+			strOpt: strOpt,
+		}
+	}
+
+	/* check for `encoding.TextMarshaler` with pointer receiver */
+	if pt.Implements(encodingTextUnmarshalerType) {
+		/* TextUnmarshal not support, string tag */
+		if strOpt {
+			panicForInvalidStrType(vt)
+		}
+		return &unmarshalTextDecoder{
+			typ: rt.UnpackType(pt),
+		}
+	}
+
+	return nil
+}
+
+func panicForInvalidStrType(vt reflect.Type) {
+	panic(error_type(rt.UnpackType(vt)))
+}

+ 60 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/const.go

@@ -0,0 +1,60 @@
+package optdec
+
+import "math"
+
+/*
+Copied from sonic-rs
+// JSON Value Type
+const NULL: u64 = 0;
+const BOOL: u64 = 2;
+const FALSE: u64 = BOOL;
+const TRUE: u64 = (1 << 3) | BOOL;
+const NUMBER: u64 = 3;
+const UINT: u64 = NUMBER;
+const SINT: u64 = (1 << 3) | NUMBER;
+const REAL: u64 = (2 << 3) | NUMBER;
+const RAWNUMBER: u64 = (3 << 3) | NUMBER;
+const STRING: u64 = 4;
+const STRING_COMMON: u64 = STRING;
+const STRING_HASESCAPED: u64 = (1 << 3) | STRING;
+const OBJECT: u64 = 6;
+const ARRAY: u64 = 7;
+
+/// JSON Type Mask
+const POS_MASK: u64 = (!0) << 32;
+const POS_BITS: u64 = 32;
+const TYPE_MASK: u64 = 0xFF;
+const TYPE_BITS: u64 = 8;
+
+*/
+
+const (
+	// BasicType: 3 bits
+	KNull   = 0 // xxxxx000
+	KBool   = 2 // xxxxx010
+	KNumber = 3 // xxxxx011
+	KString = 4 // xxxxx100
+	KRaw    = 5 // xxxxx101
+	KObject = 6 // xxxxx110
+	KArray  = 7 // xxxxx111
+
+	// SubType: 2 bits
+	KFalse            = (0 << 3) | KBool   // xxx00_010, 2
+	KTrue             = (1 << 3) | KBool   // xxx01_010, 10
+	KUint             = (0 << 3) | KNumber // xxx00_011, 3
+	KSint             = (1 << 3) | KNumber // xxx01_011, 11
+	KReal             = (2 << 3) | KNumber // xxx10_011, 19
+	KRawNumber        = (3 << 3) | KNumber // xxx11_011, 27
+	KStringCommon     = KString            // xxx00_100, 4
+	KStringEscaped = (1 << 3) | KString // xxx01_100, 12
+)
+
+const (
+	PosMask  = math.MaxUint64 << 32
+	PosBits  = 32
+	TypeMask = 0xFF
+	TypeBits = 8
+
+	ConLenMask = uint64(math.MaxUint32)
+	ConLenBits = 32
+)

+ 3 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/context.go

@@ -0,0 +1,3 @@
+package optdec
+
+type context = Context

+ 160 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/decoder.go

@@ -0,0 +1,160 @@
+package optdec
+
+import (
+	"reflect"
+	"unsafe"
+
+	"encoding/json"
+	"github.com/bytedance/sonic/internal/rt"
+	"github.com/bytedance/sonic/option"
+	"github.com/bytedance/sonic/internal/decoder/errors"
+	"github.com/bytedance/sonic/internal/decoder/consts"
+)
+
+
+type (
+	MismatchTypeError = errors.MismatchTypeError
+	SyntaxError = errors.SyntaxError
+)
+
+const (
+	_F_allow_control = consts.F_allow_control
+	_F_copy_string = consts.F_copy_string
+	_F_disable_unknown = consts.F_disable_unknown
+	_F_disable_urc = consts.F_disable_urc
+	_F_use_int64 = consts.F_use_int64
+	_F_use_number = consts.F_use_number
+	_F_validate_string = consts.F_validate_string
+)
+
+type Options = consts.Options
+
+const (
+	OptionUseInt64     = consts.OptionUseInt64
+	OptionUseNumber    = consts.OptionUseNumber
+	OptionUseUnicodeErrors = consts.OptionUseUnicodeErrors
+	OptionDisableUnknown = consts.OptionDisableUnknown
+	OptionCopyString = consts.OptionCopyString
+	OptionValidateString = consts.OptionValidateString
+)
+
+
+func Decode(s *string, i *int, f uint64, val interface{}) error {
+	vv := rt.UnpackEface(val)
+	vp := vv.Value
+
+	/* check for nil type */
+	if vv.Type == nil {
+		return &json.InvalidUnmarshalError{}
+	}
+
+	/* must be a non-nil pointer */
+	if vp == nil || vv.Type.Kind() != reflect.Ptr {
+		return &json.InvalidUnmarshalError{Type: vv.Type.Pack()}
+	}
+
+	etp := rt.PtrElem(vv.Type)
+
+	/* check the defined pointer type for issue 379 */
+	if vv.Type.IsNamed() {
+		newp := vp
+		etp = vv.Type
+		vp = unsafe.Pointer(&newp)
+	}
+
+	dec, err := findOrCompile(etp)
+	if err != nil {
+		return err
+	}
+
+	/* parse into document */
+	ctx, err := NewContext(*s, *i, uint64(f), etp)
+	defer ctx.Delete()
+	if ctx.Parser.Utf8Inv {
+		*s = ctx.Parser.Json
+	}
+	if err != nil {
+		goto fix_error;
+	}
+	err = dec.FromDom(vp, ctx.Root(), &ctx)
+
+fix_error:
+	err = fix_error(*s, *i, err)
+
+	// update position at last
+	*i += ctx.Parser.Pos()
+	return err
+}
+
+func fix_error(json string, pos int, err error) error {
+	if e, ok := err.(SyntaxError); ok {
+		return SyntaxError{
+			Pos: int(e.Pos) + pos,
+			Src: json,
+			Msg: e.Msg,
+		}
+	}
+
+	if e, ok := err.(MismatchTypeError); ok {
+		return &MismatchTypeError {
+			Pos: int(e.Pos) + pos,
+			Src: json,
+			Type: e.Type,
+		}
+	}
+
+	return err
+}
+
+// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
+// order to reduce the first-hit latency.
+//
+// Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is
+// a compile option to set the depth of recursive compile for the nested struct type.
+func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
+    cfg := option.DefaultCompileOptions()
+    for _, opt := range opts {
+        opt(&cfg)
+    }
+    return pretouchRec(map[reflect.Type]bool{vt:true}, cfg)
+}
+
+func pretouchType(_vt reflect.Type, opts option.CompileOptions) (map[reflect.Type]bool, error) {
+    /* compile function */
+    compiler := newCompiler().apply(opts)
+    decoder := func(vt *rt.GoType, _ ...interface{}) (interface{}, error) {
+        if f, err := compiler.compileType(_vt); err != nil {
+            return nil, err
+        } else {
+            return f, nil
+        }
+    }
+
+    /* find or compile */
+    vt := rt.UnpackType(_vt)
+    if val := programCache.Get(vt); val != nil {
+        return nil, nil
+    } else if _, err := programCache.Compute(vt, decoder); err == nil {
+        return compiler.visited, nil
+    } else {
+        return nil, err
+    }
+}
+
+func pretouchRec(vtm map[reflect.Type]bool, opts option.CompileOptions) error {
+    if opts.RecursiveDepth < 0 || len(vtm) == 0 {
+        return nil
+    }
+    next := make(map[reflect.Type]bool)
+    for vt := range(vtm) {
+        sub, err := pretouchType(vt, opts)
+        if err != nil {
+            return err
+        }
+        for svt := range(sub) {
+            next[svt] = true
+        }
+    }
+    opts.RecursiveDepth -= 1
+    return pretouchRec(next, opts)
+}

+ 79 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/errors.go

@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021 ByteDance Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package optdec
+
+ import (
+	 "encoding/json"
+	 "errors"
+	 "reflect"
+	 "strconv"
+ 
+	 "github.com/bytedance/sonic/internal/rt"
+ )
+
+ /** JIT Error Helpers **/
+ 
+ var stackOverflow = &json.UnsupportedValueError{
+	 Str:   "Value nesting too deep",
+	 Value: reflect.ValueOf("..."),
+ }
+ 
+ func error_type(vt *rt.GoType) error {
+	 return &json.UnmarshalTypeError{Type: vt.Pack()}
+ }
+ 
+ func error_mismatch(node Node, ctx *context, typ reflect.Type) error {
+	 return MismatchTypeError{
+		 Pos:  node.Position(),
+		 Src:  ctx.Parser.Json,
+		 Type: typ,
+	 }
+ }
+ 
+ func newUnmatched(pos int, vt *rt.GoType) error {
+	 return MismatchTypeError{
+		Pos:  pos,
+		Src:  "",
+		Type: vt.Pack(),
+	 }
+ }
+
+ func error_field(name string) error {
+	 return errors.New("json: unknown field " + strconv.Quote(name))
+ }
+ 
+ func error_value(value string, vtype reflect.Type) error {
+	 return &json.UnmarshalTypeError{
+		 Type:  vtype,
+		 Value: value,
+	 }
+ }
+ 
+ func error_syntax(pos int, src string, msg string) error {
+	 return SyntaxError{
+		 Pos: pos,
+		 Src: src,
+		 Msg: msg,
+	 }
+ }
+
+ func error_unsuppoted(typ *rt.GoType) error {
+	return &json.UnsupportedTypeError{
+		Type: typ.Pack(),
+	}
+}
+ 

+ 294 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/functor.go

@@ -0,0 +1,294 @@
+package optdec
+
+import (
+	"encoding/json"
+	"math"
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/rt"
+	"github.com/bytedance/sonic/internal/resolver"
+)
+
+type decFunc interface {
+	FromDom(vp unsafe.Pointer, node Node, ctx *context) error
+}
+
+type ptrDecoder struct {
+	typ   *rt.GoType
+	deref decFunc
+}
+
+// Pointer Value is allocated in the Caller
+func (d *ptrDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*unsafe.Pointer)(vp) = nil
+		return nil
+	}
+
+	if *(*unsafe.Pointer)(vp) == nil {
+		*(*unsafe.Pointer)(vp) = rt.Mallocgc(d.typ.Size, d.typ, true)
+	}
+
+	return d.deref.FromDom(*(*unsafe.Pointer)(vp), node, ctx)
+}
+
+type embeddedFieldPtrDecoder struct {
+	field      resolver.FieldMeta
+	fieldDec   decFunc
+	fieldName  string
+}
+
+// Pointer Value is allocated in the Caller
+func (d *embeddedFieldPtrDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	// seek into the pointer
+	vp = unsafe.Pointer(uintptr(vp) - uintptr(d.field.Path[0].Size))
+	for _, f := range d.field.Path {
+		deref := rt.UnpackType(f.Type)
+		vp = unsafe.Pointer(uintptr(vp) + f.Size)
+		if f.Kind == resolver.F_deref {
+			if  *(*unsafe.Pointer)(vp) == nil  {
+				*(*unsafe.Pointer)(vp) = rt.Mallocgc(deref.Size, deref, true)
+			}
+			vp = *(*unsafe.Pointer)(vp)
+		}
+	}
+	return d.fieldDec.FromDom(vp, node, ctx)
+}
+
+type i8Decoder struct{}
+
+func (d *i8Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	ret, ok := node.AsI64(ctx)
+	if !ok ||  ret > math.MaxInt8 || ret < math.MinInt8 {
+		return error_mismatch(node, ctx, int8Type)
+	}
+
+	*(*int8)(vp) = int8(ret)
+	return nil
+}
+
+type i16Decoder struct{}
+
+func (d *i16Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	ret, ok := node.AsI64(ctx)
+	if !ok || ret > math.MaxInt16 || ret < math.MinInt16 {
+		return error_mismatch(node, ctx, int16Type)
+	}
+
+	*(*int16)(vp) = int16(ret)
+	return nil
+}
+
+type i32Decoder struct{}
+
+func (d *i32Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	ret, ok := node.AsI64(ctx)
+	if !ok ||  ret > math.MaxInt32 || ret < math.MinInt32 {
+		return error_mismatch(node, ctx, int32Type)
+	}
+
+	*(*int32)(vp) = int32(ret)
+	return nil
+}
+
+type i64Decoder struct{}
+
+func (d *i64Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	ret, ok := node.AsI64(ctx)
+	if !ok  {
+		return error_mismatch(node, ctx, int64Type)
+	}
+
+	*(*int64)(vp) = int64(ret)
+	return nil
+}
+
+type u8Decoder struct{}
+
+func (d *u8Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	ret, ok := node.AsU64(ctx)
+	if !ok || ret > math.MaxUint8 {
+		err := error_mismatch(node, ctx, uint8Type)
+		return err
+	}
+
+	*(*uint8)(vp) = uint8(ret)
+	return nil
+}
+
+type u16Decoder struct{}
+
+func (d *u16Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	ret, ok := node.AsU64(ctx)
+	if !ok || ret > math.MaxUint16 {
+		return error_mismatch(node, ctx, uint16Type)
+	}
+	*(*uint16)(vp) = uint16(ret)
+	return nil
+}
+
+type u32Decoder struct{}
+
+func (d *u32Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	ret, ok := node.AsU64(ctx)
+	if !ok || ret > math.MaxUint32 {
+		return error_mismatch(node, ctx, uint32Type)
+	}
+
+	*(*uint32)(vp) = uint32(ret)
+	return nil
+}
+
+type u64Decoder struct{}
+
+func (d *u64Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	ret, ok := node.AsU64(ctx)
+	if !ok {
+		return error_mismatch(node, ctx, uint64Type)
+	}
+
+	*(*uint64)(vp) = uint64(ret)
+	return nil
+}
+
+type f32Decoder struct{}
+
+func (d *f32Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	ret, ok := node.AsF64(ctx)
+	if !ok || ret > math.MaxFloat32 || ret < -math.MaxFloat32 {
+		return error_mismatch(node, ctx, float32Type)
+	}
+
+	*(*float32)(vp) = float32(ret)
+	return nil
+}
+
+type f64Decoder struct{}
+
+func (d *f64Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	ret, ok := node.AsF64(ctx)
+	if !ok {
+		return  error_mismatch(node, ctx, float64Type)
+	}
+
+	*(*float64)(vp) = float64(ret)
+	return nil
+}
+
+type boolDecoder struct {
+}
+
+func (d *boolDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	ret, ok := node.AsBool()
+	if !ok {
+		return error_mismatch(node, ctx, boolType)
+	}
+
+	*(*bool)(vp) = bool(ret)
+	return nil
+}
+
+type stringDecoder struct {
+}
+
+func (d *stringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	ret, ok := node.AsStr(ctx)
+	if !ok {
+		return error_mismatch(node, ctx, stringType)
+	}
+	*(*string)(vp) = ret
+	return nil
+}
+
+type numberDecoder struct {
+}
+
+func (d *numberDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	num, ok := node.AsNumber(ctx)
+	if !ok {
+		return error_mismatch(node, ctx, jsonNumberType)
+	}
+	*(*json.Number)(vp) = num
+	return nil
+}
+
+type recuriveDecoder struct {
+	typ *rt.GoType
+}
+
+func (d *recuriveDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	dec, err := findOrCompile(d.typ)
+	if err != nil {
+		return err
+	}
+	return dec.FromDom(vp, node, ctx)
+}
+
+type unsupportedTypeDecoder struct {
+	typ *rt.GoType
+}
+
+
+func (d *unsupportedTypeDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+	return error_unsuppoted(d.typ)
+}
+

+ 110 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/helper.go

@@ -0,0 +1,110 @@
+package optdec
+
+import (
+	"encoding/json"
+	"strconv"
+
+	"github.com/bytedance/sonic/internal/native"
+	"github.com/bytedance/sonic/internal/utils"
+	"github.com/bytedance/sonic/internal/native/types"
+)
+
+
+func SkipNumberFast(json string, start int) (int, bool) {
+	// find the number ending, we parsed in native, it always valid
+	pos := start
+	for pos < len(json) && json[pos] != ']' && json[pos] != '}' && json[pos] != ',' {
+		if json[pos] >= '0' && json[pos] <= '9' || json[pos] == '.' || json[pos] == '-' || json[pos] == '+' || json[pos] == 'e' || json[pos] == 'E' {
+			pos += 1
+		} else {
+			break
+		}
+	}
+
+	// if not found number, return false
+	if pos == start {
+		return pos, false
+	}
+	return pos, true
+}
+
+
+func isSpace(c byte) bool {
+    return c == ' ' || c == '\t' || c == '\n' || c == '\r'
+}
+
+// pos is the start index of the raw
+func ValidNumberFast(raw string) bool {
+	ret := utils.SkipNumber(raw, 0)
+	if ret < 0 {
+		return false
+	}
+
+	// check trailing chars
+	for ret < len(raw) {
+		return false
+	}
+
+	return true
+}
+
+func SkipOneFast2(json string, pos *int) (int, error) {
+	// find the number ending, we parsed in sonic-cpp, it always valid
+	start := native.SkipOneFast(&json, pos)
+	if start < 0 {
+		return -1, error_syntax(*pos, json, types.ParsingError(-start).Error())
+	}
+	return start, nil
+}
+
+func SkipOneFast(json string, pos int) (string, error) {
+	// find the number ending, we parsed in sonic-cpp, it always valid
+	start := native.SkipOneFast(&json, &pos)
+	if start < 0 {
+		// TODO: details error code
+		return "", error_syntax(pos, json, types.ParsingError(-start).Error())
+	}
+	return json[start:pos], nil
+}
+
+func ParseI64(raw string) (int64, error) {
+	i64, err := strconv.ParseInt(raw, 10, 64)
+	if err != nil {
+		return 0, err
+	}
+	return i64, nil
+}
+
+func ParseBool(raw string) (bool, error) {
+	var b bool
+	err := json.Unmarshal([]byte(raw), &b)
+	if err != nil {
+		return false, err
+	}
+	return b, nil
+}
+
+func ParseU64(raw string) (uint64, error) {
+	u64, err := strconv.ParseUint(raw, 10, 64)
+	if err != nil {
+		return 0, err
+	}
+	return u64, nil
+}
+
+func ParseF64(raw string) (float64, error) {
+	f64, err := strconv.ParseFloat(raw, 64)
+	if err != nil {
+		return 0, err
+	}
+	return f64, nil
+}
+
+func Unquote(raw string) (string, error) {
+	var u string
+	err := json.Unmarshal([]byte(raw), &u)
+	if err != nil {
+		return "", err
+	}
+	return u, nil
+}

+ 172 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/interface.go

@@ -0,0 +1,172 @@
+package optdec
+
+import (
+	"encoding"
+	"encoding/json"
+	"unsafe"
+	"reflect"
+
+	"github.com/bytedance/sonic/internal/rt"
+)
+
+type efaceDecoder struct {
+}
+
+func (d *efaceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	/* check the defined pointer type for issue 379 */
+	eface := (*rt.GoEface)(vp)
+
+	/*
+	 not pointer type, or nil pointer, or self-pointed interface{}, such as 
+		```go
+		var v interface{}
+		v = &v
+		return v
+		``` see `issue758_test.go`.
+	*/
+	if eface.Value == nil || eface.Type.Kind() != reflect.Ptr || eface.Value == vp {
+		ret, err := node.AsEface(ctx)
+		if err != nil {
+			return err
+		}
+		*(*interface{})(vp) = ret
+		return nil
+	}
+
+	if node.IsNull() {
+		if eface.Type.Indirect() || (!eface.Type.Indirect() &&  eface.Type.Pack().Elem().Kind() != reflect.Ptr) {
+			*(*interface{})(vp) = nil
+			return nil
+		}
+	}
+
+	etp := rt.PtrElem(eface.Type)
+	vp = eface.Value
+
+	if eface.Type.IsNamed() {
+		// check named pointer type, avoid call its `Unmarshaler`
+		newp := vp
+		etp = eface.Type
+		vp = unsafe.Pointer(&newp)
+	} else if !eface.Type.Indirect() {
+		// check direct value
+		etp = rt.UnpackType(eface.Type.Pack().Elem())
+	}
+
+	dec, err := findOrCompile(etp)
+	if err != nil {
+		return err
+	}
+
+	return dec.FromDom(vp, node, ctx)
+}
+
+type ifaceDecoder struct {
+	typ *rt.GoType
+}
+
+func (d *ifaceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*unsafe.Pointer)(vp) = nil
+		return nil
+	}
+
+	iface := *(*rt.GoIface)(vp)
+	if iface.Itab == nil {
+		return error_type(d.typ)
+	}
+
+	vt := iface.Itab.Vt
+	if vt.Kind() != reflect.Ptr || iface.Value == nil {
+		return error_type(d.typ)
+	}
+
+	etp := rt.PtrElem(vt)
+	vp = iface.Value
+
+	/* check the defined pointer type for issue 379 */
+	if vt.IsNamed() {
+		newp := vp
+		etp = vt
+		vp = unsafe.Pointer(&newp)
+	}
+
+	dec, err := findOrCompile(etp)
+	if err != nil {
+		return err
+	}
+
+	return dec.FromDom(vp, node, ctx)
+}
+
+type unmarshalTextDecoder struct {
+	typ *rt.GoType
+}
+
+func (d *unmarshalTextDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*unsafe.Pointer)(vp) = nil
+		return nil
+	}
+
+	txt, ok := node.AsStringText(ctx)
+	if !ok {
+		return error_mismatch(node, ctx, d.typ.Pack())
+	}
+
+	v := *(*interface{})(unsafe.Pointer(&rt.GoEface{
+		Type:  d.typ,
+		Value: vp,
+	}))
+
+	// fast path
+	if u, ok :=  v.(encoding.TextUnmarshaler); ok {
+		return u.UnmarshalText(txt)
+	}
+
+	// slow path
+	rv := reflect.ValueOf(v)
+	if u, ok := rv.Interface().(encoding.TextUnmarshaler); ok {
+		return u.UnmarshalText(txt)
+	}
+
+	return error_type(d.typ)
+}
+
+type unmarshalJSONDecoder struct {
+	typ 	*rt.GoType
+	strOpt	bool
+}
+
+func (d *unmarshalJSONDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	v := *(*interface{})(unsafe.Pointer(&rt.GoEface{
+		Type: d.typ,
+		Value: vp,
+	}))
+
+	var input []byte
+	if d.strOpt && node.IsNull() {
+		input = []byte("null")
+	} else if d.strOpt {
+		s, ok := node.AsStringText(ctx)
+		if !ok {
+			return error_mismatch(node, ctx, d.typ.Pack())
+		}
+		input = s
+	} else {
+		input = []byte(node.AsRaw(ctx))
+	}
+
+	// fast path
+	if u, ok :=  v.(json.Unmarshaler); ok {
+		return u.UnmarshalJSON((input))
+	}
+
+	// slow path
+	rv := reflect.ValueOf(v)
+	if u, ok := rv.Interface().(json.Unmarshaler); ok {
+		return u.UnmarshalJSON(input)
+	}
+
+	return error_type(d.typ)
+}

+ 458 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/map.go

@@ -0,0 +1,458 @@
+package optdec
+
+import (
+	"encoding"
+	"encoding/json"
+	"math"
+	"reflect"
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/rt"
+)
+
+/** Decoder for most common map types: map[string]interface{}, map[string]string **/
+
+type mapEfaceDecoder struct {
+}
+
+func (d *mapEfaceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*map[string]interface{})(vp) = nil
+		return nil
+	}
+
+	return node.AsMapEface(ctx, vp)
+}
+
+type mapStringDecoder struct {
+}
+
+func (d *mapStringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*map[string]string)(vp) = nil
+		return nil
+	}
+
+	return node.AsMapString(ctx, vp)
+}
+
+/** Decoder for map with string key **/
+
+type mapStrKeyDecoder struct {
+	mapType *rt.GoMapType
+	elemDec decFunc
+	assign  rt.MapStrAssign
+	typ 	reflect.Type
+}
+
+func (d *mapStrKeyDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*unsafe.Pointer)(vp) = nil
+		return nil
+	}
+
+	obj, ok := node.AsObj()
+	if !ok {
+		return error_mismatch(node, ctx, d.mapType.Pack())
+	}
+
+	// allocate map
+	m := *(*unsafe.Pointer)(vp)
+	if m == nil {
+		m = rt.Makemap(&d.mapType.GoType, obj.Len())
+	}
+
+	var gerr error
+	next := obj.Children()
+	for i := 0; i < obj.Len(); i++ {
+		keyn := NewNode(next)
+		key, _ := keyn.AsStr(ctx)
+
+		valn := NewNode(PtrOffset(next, 1))
+		valp := d.assign(d.mapType, m, key)
+		err := d.elemDec.FromDom(valp, valn, ctx)
+		if gerr == nil && err != nil {
+			gerr = err
+		}
+		next = valn.Next()
+	}
+
+	*(*unsafe.Pointer)(vp) = m
+	return gerr
+}
+
+/** Decoder for map with int32 or int64 key **/
+
+type mapI32KeyDecoder struct {
+	mapType *rt.GoMapType
+	elemDec decFunc
+	assign rt.Map32Assign
+}
+
+func (d *mapI32KeyDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*unsafe.Pointer)(vp) = nil
+		return nil
+	}
+
+	obj, ok := node.AsObj()
+	if !ok {
+		return error_mismatch(node, ctx, d.mapType.Pack())
+	}
+
+	// allocate map
+	m := *(*unsafe.Pointer)(vp)
+	if m == nil {
+		m = rt.Makemap(&d.mapType.GoType, obj.Len())
+	}
+
+	next := obj.Children()
+	var gerr error
+	for i := 0; i < obj.Len(); i++ {
+		keyn := NewNode(next)
+		k, ok := keyn.ParseI64(ctx)
+		if !ok || k > math.MaxInt32 || k < math.MinInt32 {
+			if gerr == nil {
+				gerr = error_mismatch(keyn, ctx, d.mapType.Pack())
+			}
+			valn := NewNode(PtrOffset(next, 1))
+			next = valn.Next()
+			continue
+		}
+
+		key := int32(k)
+		ku32 := *(*uint32)(unsafe.Pointer(&key))
+		valn := NewNode(PtrOffset(next, 1))
+		valp := d.assign(d.mapType, m, ku32)
+		err := d.elemDec.FromDom(valp, valn, ctx)
+		if gerr == nil && err != nil {
+			gerr = err
+		}
+
+		next = valn.Next()
+	}
+
+	*(*unsafe.Pointer)(vp) = m
+	return gerr
+}
+
+type mapI64KeyDecoder struct {
+	mapType *rt.GoMapType
+	elemDec decFunc
+	assign rt.Map64Assign
+}
+
+func (d *mapI64KeyDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*unsafe.Pointer)(vp) = nil
+		return nil
+	}
+
+	obj, ok := node.AsObj()
+	if !ok {
+		return error_mismatch(node, ctx, d.mapType.Pack())
+	}
+
+	// allocate map
+	m := *(*unsafe.Pointer)(vp)
+	if m == nil {
+		m = rt.Makemap(&d.mapType.GoType, obj.Len())
+	}
+
+	var gerr error
+	next := obj.Children()
+	for i := 0; i < obj.Len(); i++ {
+		keyn := NewNode(next)
+		key, ok := keyn.ParseI64(ctx)
+
+		if !ok {
+			if gerr == nil {
+				gerr = error_mismatch(keyn, ctx, d.mapType.Pack())
+			}
+			valn := NewNode(PtrOffset(next, 1))
+			next = valn.Next()
+			continue
+		}
+
+		ku64 := *(*uint64)(unsafe.Pointer(&key))
+		valn := NewNode(PtrOffset(next, 1))
+		valp := d.assign(d.mapType, m, ku64)
+		err := d.elemDec.FromDom(valp, valn, ctx)
+		if gerr == nil && err != nil {
+			gerr = err
+		}
+		next = valn.Next()
+	}
+
+	*(*unsafe.Pointer)(vp) = m
+	return gerr
+}
+
+/** Decoder for map with unt32 or uint64 key **/
+
+type mapU32KeyDecoder struct {
+	mapType *rt.GoMapType
+	elemDec decFunc
+	assign  rt.Map32Assign
+}
+
+func (d *mapU32KeyDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*unsafe.Pointer)(vp) = nil
+		return nil
+	}
+
+	obj, ok := node.AsObj()
+	if !ok {
+		return error_mismatch(node, ctx, d.mapType.Pack())
+	}
+
+	// allocate map
+	m := *(*unsafe.Pointer)(vp)
+	if m == nil {
+		m = rt.Makemap(&d.mapType.GoType, obj.Len())
+	}
+
+	var gerr error
+	next := obj.Children()
+	for i := 0; i < obj.Len(); i++ {
+		keyn := NewNode(next)
+		k, ok := keyn.ParseU64(ctx)
+		if !ok || k > math.MaxUint32 {
+			if gerr == nil {
+				gerr = error_mismatch(keyn, ctx, d.mapType.Pack())
+			}
+			valn := NewNode(PtrOffset(next, 1))
+			next = valn.Next()
+			continue
+		}
+
+		key := uint32(k)
+		valn := NewNode(PtrOffset(next, 1))
+		valp := d.assign(d.mapType, m, key)
+		err := d.elemDec.FromDom(valp, valn, ctx)
+		if gerr == nil && err != nil {
+			gerr = err
+		}
+		next = valn.Next()
+	}
+
+	*(*unsafe.Pointer)(vp) = m
+	return gerr
+}
+
+type mapU64KeyDecoder struct {
+	mapType *rt.GoMapType
+	elemDec decFunc
+	assign rt.Map64Assign
+}
+
+func (d *mapU64KeyDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*unsafe.Pointer)(vp) = nil
+		return nil
+	}
+
+	obj, ok := node.AsObj()
+	if !ok {
+		return  error_mismatch(node, ctx, d.mapType.Pack())
+	}
+	// allocate map
+	m := *(*unsafe.Pointer)(vp)
+	if m == nil {
+		m = rt.Makemap(&d.mapType.GoType, obj.Len())
+	}
+
+	var gerr error
+	next := obj.Children()
+	for i := 0; i < obj.Len(); i++ {
+		keyn := NewNode(next)
+		key, ok := keyn.ParseU64(ctx)
+		if !ok {
+			if gerr == nil {
+				gerr = error_mismatch(keyn, ctx, d.mapType.Pack())
+			}
+			valn := NewNode(PtrOffset(next, 1))
+			next = valn.Next()
+			continue
+		}
+
+		valn := NewNode(PtrOffset(next, 1))
+		valp := d.assign(d.mapType, m, key)
+		err := d.elemDec.FromDom(valp, valn, ctx)
+		if gerr == nil && err != nil {
+			gerr = err
+		}
+		next = valn.Next()
+	}
+
+	*(*unsafe.Pointer)(vp) = m
+	return gerr
+}
+
+/** Decoder for generic cases */
+
+type decKey func(dec *mapDecoder, raw string) (interface{}, error)
+
+func decodeKeyU8(dec *mapDecoder, raw string) (interface{}, error) {
+	key, err := Unquote(raw)
+	if err != nil {
+		return nil, err
+	}
+	ret, err := ParseU64(key)
+	if err != nil {
+		return nil, err
+	}
+	if ret > math.MaxUint8 {
+		return nil, error_value(key, dec.mapType.Key.Pack())
+	}
+	return uint8(ret), nil
+}
+
+func decodeKeyU16(dec *mapDecoder, raw string) (interface{}, error) {
+	key, err := Unquote(raw)
+	if err != nil {
+		return nil, err
+	}
+	ret, err := ParseU64(key)
+	if err != nil {
+		return nil, err
+	}
+	if ret > math.MaxUint16 {
+		return nil, error_value(key, dec.mapType.Key.Pack())
+	}
+	return uint16(ret), nil
+}
+
+func decodeKeyI8(dec *mapDecoder, raw string) (interface{}, error) {
+	key, err := Unquote(raw)
+	if err != nil {
+		return nil, err
+	}
+	ret, err := ParseI64(key)
+	if err != nil {
+		return nil, err
+	}
+	if ret > math.MaxInt8 || ret < math.MinInt8 {
+		return nil, error_value(key, dec.mapType.Key.Pack())
+	}
+	return int8(ret), nil
+}
+
+func decodeKeyI16(dec *mapDecoder, raw string) (interface{}, error) {
+	key, err := Unquote(raw)
+	if err != nil {
+		return nil, err
+	}
+	ret, err := ParseI64(key)
+	if err != nil {
+		return nil, err
+	}
+	if ret > math.MaxInt16 || ret < math.MinInt16 {
+		return nil, error_value(key, dec.mapType.Key.Pack())
+	}
+	return int16(ret), nil
+}
+
+func decodeKeyTextUnmarshaler(dec *mapDecoder, raw string) (interface{}, error) {
+	key, err := Unquote(raw)
+	if err != nil {
+		return nil, err
+	}
+	ret := reflect.New(dec.mapType.Key.Pack()).Interface()
+	err = ret.(encoding.TextUnmarshaler).UnmarshalText(rt.Str2Mem(key))
+	if err != nil {
+		return nil, err
+	}
+	return ret, nil
+}
+
+func decodeFloat32Key(dec *mapDecoder, raw string) (interface{}, error) {
+	key, err := Unquote(raw)
+	if err != nil {
+		return nil, err
+	}
+	ret, err := ParseF64(key)
+	if err != nil {
+		return nil, err
+	}
+	if ret > math.MaxFloat32 || ret < -math.MaxFloat32 {
+		return nil, error_value(key, dec.mapType.Key.Pack())
+	}
+	return float32(ret), nil
+}
+
+func decodeFloat64Key(dec *mapDecoder, raw string) (interface{}, error) {
+	key, err := Unquote(raw)
+	if err != nil {
+		return nil, err
+	}
+	return ParseF64(key)
+}
+
+func decodeJsonNumberKey(dec *mapDecoder, raw string) (interface{}, error) {
+	// skip the quote
+	raw = raw[1:len(raw)-1]
+	end, ok := SkipNumberFast(raw, 0)
+
+	// check trailing chars
+	if !ok || end != len(raw) {
+		return nil, error_value(raw, rt.JsonNumberType.Pack())
+	}
+	
+	return json.Number(raw[0:end]), nil
+}
+
+type mapDecoder struct {
+	mapType *rt.GoMapType
+	keyDec  decKey
+	elemDec decFunc
+}
+
+func (d *mapDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*unsafe.Pointer)(vp) = nil
+		return nil
+	}
+
+	obj, ok := node.AsObj()
+	if !ok || d.keyDec == nil {
+		return error_mismatch(node, ctx, d.mapType.Pack())
+	}
+
+	// allocate map
+	m := *(*unsafe.Pointer)(vp)
+	if m == nil {
+		m = rt.Makemap(&d.mapType.GoType, obj.Len())
+	}
+
+	next := obj.Children()
+	var gerr error
+	for i := 0; i < obj.Len(); i++ {
+		keyn := NewNode(next)
+		raw := keyn.AsRaw(ctx)
+
+		key, err := d.keyDec(d, raw)
+		if err != nil {
+			if gerr == nil {
+				gerr = error_mismatch(keyn, ctx, d.mapType.Pack())
+			}
+			valn := NewNode(PtrOffset(next, 1))
+			next = valn.Next()
+			continue
+		}
+
+		valn := NewNode(PtrOffset(next, 1))
+		keyp := rt.UnpackEface(key).Value
+		valp := rt.Mapassign(d.mapType, m, keyp)
+		err = d.elemDec.FromDom(valp, valn, ctx)
+		if gerr == nil && err != nil {
+			gerr = err
+		}
+
+		next = valn.Next()
+	}
+
+	*(*unsafe.Pointer)(vp) = m
+	return gerr
+}

+ 270 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/native.go

@@ -0,0 +1,270 @@
+package optdec
+
+import (
+	"fmt"
+	"reflect"
+	"unsafe"
+
+	"sync"
+
+	"github.com/bytedance/sonic/internal/native"
+	"github.com/bytedance/sonic/internal/native/types"
+	"github.com/bytedance/sonic/internal/rt"
+	"github.com/bytedance/sonic/utf8"
+)
+
+
+type ErrorCode int
+
+const (
+	SONIC_OK                = 0;
+	SONIC_CONTROL_CHAR      = 1;
+	SONIC_INVALID_ESCAPED   = 2;
+	SONIC_INVALID_NUM       = 3;
+	SONIC_FLOAT_INF         = 4;
+	SONIC_EOF               = 5;
+	SONIC_INVALID_CHAR   	= 6;
+	SONIC_EXPECT_KEY        = 7;
+	SONIC_EXPECT_COLON      = 8;
+	SONIC_EXPECT_OBJ_COMMA_OR_END  = 9;
+	SONIC_EXPECT_ARR_COMMA_OR_END  = 10;
+	SONIC_VISIT_FAILED        = 11;
+	SONIC_INVALID_ESCAPED_UTF = 12;
+	SONIC_INVALID_LITERAL 	  = 13;
+	SONIC_STACK_OVERFLOW	  = 14;
+)
+
+var ParsingErrors = []string{
+    SONIC_OK                      	: "ok",
+	SONIC_CONTROL_CHAR      	  	: "control chars in string",
+	SONIC_INVALID_ESCAPED 		  	: "invalid escaped chars in string",
+	SONIC_INVALID_NUM       		: "invalid number",
+	SONIC_FLOAT_INF         		: "float infinity",
+	SONIC_EOF               		: "eof",
+	SONIC_INVALID_CHAR		  		: "invalid chars",
+	SONIC_EXPECT_KEY        		: "expect a json key",
+	SONIC_EXPECT_COLON      		: "expect a `:`",
+	SONIC_EXPECT_OBJ_COMMA_OR_END	: "expect a `,` or `}`",
+	SONIC_EXPECT_ARR_COMMA_OR_END	: "expect a `,` or `]`",
+	SONIC_VISIT_FAILED     			: "failed in json visitor",
+	SONIC_INVALID_ESCAPED_UTF		: "invalid escaped unicodes",
+	SONIC_INVALID_LITERAL			: "invalid literal(true/false/null)",
+	SONIC_STACK_OVERFLOW			: "json is exceeded max depth 4096, cause stack overflow",
+}
+
+func (code ErrorCode) Error() string {
+	return ParsingErrors[code]
+}
+
+type node struct {
+	typ uint64
+	val uint64
+}
+
+// should consistent with native/parser.c
+type _nospaceBlock struct {
+	_ [8]byte
+	_ [8]byte
+}
+
+// should consistent with native/parser.c
+type nodeBuf struct {
+	ncur    uintptr
+	parent  int64
+	depth   uint64
+	nstart  uintptr
+	nend    uintptr
+	iskey   bool
+	stat    jsonStat
+}
+
+func (self *nodeBuf) init(nodes []node) {
+	self.ncur = uintptr(unsafe.Pointer(&nodes[0]))
+	self.nstart = self.ncur
+	self.nend = self.ncur + uintptr(cap(nodes)) * unsafe.Sizeof(node{})
+	self.parent = -1
+}
+
+// should consistent with native/parser.c
+type Parser struct {
+	Json    string
+	padded	[]byte
+	nodes 	[]node
+	dbuf 	[]byte
+	backup  []node
+
+	options uint64
+	// JSON cursor
+	start   uintptr
+	cur     uintptr
+	end     uintptr
+	_nbk    _nospaceBlock
+
+	// node buffer cursor
+	nbuf   	nodeBuf
+	Utf8Inv  	bool
+	isEface    bool
+}
+
+// only when parse non-empty object/array are needed.
+type jsonStat struct {
+    object 		uint32
+    array 		uint32
+    str 		uint32
+    number 		uint32
+    array_elems uint32
+    object_keys uint32
+    max_depth	uint32
+}
+
+
+var (
+	defaultJsonPaddedCap uintptr =  1 << 20  // 1 Mb
+	defaultNodesCap      uintptr =  (1 << 20) / unsafe.Sizeof(node{})  // 1 Mb
+)
+
+var parsePool sync.Pool = sync.Pool {
+	New: func () interface{} {
+		return &Parser{
+			options: 0,
+			padded: make([]byte, 0, defaultJsonPaddedCap),
+			nodes: make([]node, defaultNodesCap, defaultNodesCap),
+			dbuf: make([]byte, types.MaxDigitNums, types.MaxDigitNums),
+		}
+	},
+}
+
+var padding string = "x\"x\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+
+func newParser(data string, pos int, opt uint64) *Parser {
+	p := parsePool.Get().(*Parser)
+
+	/* validate json if needed */
+	if (opt & (1 << _F_validate_string)) != 0  && !utf8.ValidateString(data){
+		dbuf := utf8.CorrectWith(nil, rt.Str2Mem(data[pos:]), "\ufffd")
+		dbuf = append(dbuf, padding...)
+		p.Json = rt.Mem2Str(dbuf[:len(dbuf) - len(padding)])
+		p.Utf8Inv = true
+		p.start = uintptr((*rt.GoString)(unsafe.Pointer(&p.Json)).Ptr)
+	} else {
+		p.Json = data
+		// TODO: prevent too large JSON
+		p.padded = append(p.padded, data[pos:]...)
+		p.padded = append(p.padded, padding...)
+		p.start = uintptr((*rt.GoSlice)(unsafe.Pointer(&p.padded)).Ptr)
+	}
+
+	p.cur 	= p.start
+	p.end   = p.cur + uintptr(len(p.Json))
+	p.options = opt
+	p.nbuf.init(p.nodes)
+	return p
+}
+
+
+func (p *Parser) Pos() int {
+	return int(p.cur - p.start)
+}
+
+func (p *Parser) JsonBytes() []byte {
+	if p.Utf8Inv {
+		return (rt.Str2Mem(p.Json))
+	} else {
+		return p.padded
+	}
+}
+
+var nodeType = rt.UnpackType(reflect.TypeOf(node{}))
+
+//go:inline
+func calMaxNodeCap(jsonSize int) int {
+	return jsonSize / 2 + 2
+}
+
+func (p *Parser) parse() ErrorCode {
+	// when decode into struct, we should decode number as possible
+	old := p.options
+	if !p.isEface {
+		p.options &^= 1 << _F_use_number
+	}
+
+	// fast path with limited node buffer
+	err := ErrorCode(native.ParseWithPadding(unsafe.Pointer(p)))
+	if err != SONIC_VISIT_FAILED {
+		p.options = old
+		return err
+	}
+
+	// check OoB here
+	offset := p.nbuf.ncur - p.nbuf.nstart
+	curLen :=  int(offset / unsafe.Sizeof(node{}))
+	if curLen != len(p.nodes) {
+		panic(fmt.Sprintf("current len: %d, real len: %d cap: %d", curLen, len(p.nodes), cap(p.nodes)))
+	}
+
+	// node buf is not enough, continue parse
+	// the maxCap is always meet all valid JSON
+	maxCap := curLen + calMaxNodeCap(len(p.Json) - int(p.cur - p.start))
+	slice := rt.GoSlice{
+		Ptr: rt.Mallocgc(uintptr(maxCap) * nodeType.Size, nodeType, false),
+		Len: maxCap,
+		Cap: maxCap,
+	}
+	rt.Memmove(unsafe.Pointer(slice.Ptr), unsafe.Pointer(&p.nodes[0]), offset)
+	p.backup = p.nodes
+	p.nodes = *(*[]node)(unsafe.Pointer(&slice))
+
+	// update node cursor
+	p.nbuf.nstart = uintptr(unsafe.Pointer(&p.nodes[0]))
+	p.nbuf.nend = p.nbuf.nstart + uintptr(cap(p.nodes)) * unsafe.Sizeof(node{})
+	p.nbuf.ncur = p.nbuf.nstart + offset
+
+	// continue parse json
+	err = ErrorCode(native.ParseWithPadding(unsafe.Pointer(p)))
+	p.options = old
+	return err
+}
+
+func (p *Parser) reset() {
+	p.options = 0
+	p.padded = p.padded[:0]
+	// nodes is too large here, we will not reset it and use small backup nodes buffer
+	if p.backup != nil {
+		p.nodes = p.backup
+		p.backup = nil
+	}
+	p.start = 0
+	p.cur = 0
+	p.end = 0
+	p.Json = ""
+	p.nbuf = nodeBuf{}
+	p._nbk = _nospaceBlock{}
+	p.Utf8Inv = false
+	p.isEface = false
+}
+
+func (p *Parser) free() {
+	p.reset()
+	parsePool.Put(p)
+}
+
+//go:noinline
+func (p *Parser) fixError(code ErrorCode) error {
+	if code == SONIC_OK {
+		return nil
+	}
+
+	if p.Pos() == 0 {
+		code = SONIC_EOF;
+	}
+
+	pos := p.Pos() - 1
+	return error_syntax(pos, p.Json, ParsingErrors[code])
+}
+
+func Parse(data string, opt uint64) error {
+	p := newParser(data, 0, opt)
+	err := p.parse()
+	p.free()
+	return err
+}

+ 1298 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/node.go

@@ -0,0 +1,1298 @@
+package optdec
+
+import (
+	"encoding/json"
+	"math"
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/envs"
+	"github.com/bytedance/sonic/internal/rt"
+)
+
+type Context struct {
+	Parser      *Parser
+	efacePool   *efacePool
+	Stack       bounedStack
+	Utf8Inv     bool
+}
+
+func (ctx *Context) Options() uint64 {
+	return ctx.Parser.options
+}
+
+/************************* Stack and Pool Helper *******************/
+
+type parentStat struct {
+	con 	unsafe.Pointer
+	remain	uint64
+}
+type bounedStack struct {
+	stack []parentStat
+	index int
+}
+
+func newStack(size int) bounedStack {
+	return bounedStack{
+		stack: make([]parentStat, size + 2),
+		index: 0,
+	}
+}
+
+//go:nosplit
+func (s *bounedStack) Pop() (unsafe.Pointer, int, bool){
+	s.index--
+	con := s.stack[s.index].con
+	remain := s.stack[s.index].remain &^ (uint64(1) << 63)
+	isObj := (s.stack[s.index].remain & (uint64(1) << 63)) != 0
+	s.stack[s.index].con = nil
+	s.stack[s.index].remain = 0
+	return con, int(remain), isObj
+}
+
+//go:nosplit
+func (s *bounedStack) Push(p unsafe.Pointer, remain int, isObj bool) {
+	s.stack[s.index].con = p
+	s.stack[s.index].remain = uint64(remain)
+	if isObj {
+		s.stack[s.index].remain |= (uint64(1) << 63)
+	}
+	s.index++
+}
+
+type efacePool struct{
+	t64   		rt.T64Pool
+	tslice 		rt.TslicePool
+	tstring 	rt.TstringPool
+	efaceSlice  rt.SlicePool
+}
+
+func newEfacePool(stat *jsonStat, useNumber bool) *efacePool {
+	strs := int(stat.str)
+	nums := 0
+	if useNumber {
+		strs += int(stat.number)
+	} else {
+		nums = int(stat.number)
+	}
+
+	return &efacePool{
+		t64: rt.NewT64Pool(nums),
+		tslice: rt.NewTslicePool(int(stat.array)),
+		tstring: rt.NewTstringPool(strs),
+		efaceSlice: rt.NewPool(rt.AnyType, int(stat.array_elems)),
+	}
+}
+
+func (self *efacePool) GetMap(hint int) unsafe.Pointer {
+	m := make(map[string]interface{}, hint)
+	return *(*unsafe.Pointer)(unsafe.Pointer(&m))
+}
+
+func (self *efacePool) GetSlice(hint int) unsafe.Pointer {
+	return unsafe.Pointer(self.efaceSlice.GetSlice(hint))
+}
+
+func (self *efacePool) ConvTSlice(val rt.GoSlice, typ *rt.GoType,  dst unsafe.Pointer) {
+	self.tslice.Conv(val, typ, (*interface{})(dst))
+}
+
+func (self *efacePool) ConvF64(val float64, dst unsafe.Pointer) {
+	self.t64.Conv(castU64(val), rt.Float64Type, (*interface{})(dst))
+}
+
+func (self *efacePool) ConvTstring(val string, dst unsafe.Pointer) {
+	self.tstring.Conv(val, (*interface{})(dst))
+}
+
+func (self *efacePool) ConvTnum(val json.Number, dst unsafe.Pointer) {
+	self.tstring.ConvNum(val, (*interface{})(dst))
+}
+
+/********************************************************/
+
+func canUseFastMap( opts uint64, root *rt.GoType) bool {
+	return envs.UseFastMap && (opts & (1 << _F_copy_string)) == 0 &&  (opts & (1 << _F_use_int64)) == 0  && (root == rt.AnyType || root == rt.MapEfaceType || root == rt.SliceEfaceType) 
+}
+
+func NewContext(json string, pos int, opts uint64, root *rt.GoType) (Context, error) {
+	ctx := Context{
+		Parser: newParser(json, pos, opts),
+	}
+	if root == rt.AnyType || root == rt.MapEfaceType || root == rt.SliceEfaceType {
+		ctx.Parser.isEface = true
+	}
+
+	ecode := ctx.Parser.parse()
+
+	if ecode != 0 {
+		return ctx, ctx.Parser.fixError(ecode)
+	}
+
+	useNumber := (opts & (1 << _F_use_number )) != 0
+	if canUseFastMap(opts, root) {
+		ctx.efacePool = newEfacePool(&ctx.Parser.nbuf.stat, useNumber)
+		ctx.Stack = newStack(int(ctx.Parser.nbuf.stat.max_depth))
+	}
+
+	return ctx, nil
+}
+
+func (ctx *Context) Delete() {
+	ctx.Parser.free()
+	ctx.Parser = nil
+}
+
+type Node struct {
+	cptr uintptr
+}
+
+func NewNode(cptr uintptr) Node {
+	return Node{cptr: cptr}
+}
+
+type Dom struct {
+	cdom uintptr
+}
+
+func (ctx *Context) Root() Node {
+	root := (uintptr)(((*rt.GoSlice)(unsafe.Pointer(&ctx.Parser.nodes))).Ptr)
+	return Node{cptr: root}
+}
+
+type Array struct {
+	cptr uintptr
+}
+
+type Object struct {
+	cptr uintptr
+}
+
+func (obj Object) Len() int {
+	cobj := ptrCast(obj.cptr)
+	return int(uint64(cobj.val) & ConLenMask)
+}
+
+func (arr Array) Len() int {
+	carr :=  ptrCast(arr.cptr)
+	return int(uint64(carr.val) & ConLenMask)
+}
+
+// / Helper functions to eliminate CGO calls
+func (val Node) Type() uint8 {
+	ctype := ptrCast(val.cptr)
+	return uint8(ctype.typ & TypeMask)
+}
+
+func (val Node) Next() uintptr {
+	if val.Type() != KObject && val.Type() != KArray {
+		return PtrOffset(val.cptr, 1)
+	}
+	cobj := ptrCast(val.cptr)
+	offset := int64(uint64(cobj.val) >> ConLenBits)
+	return PtrOffset(val.cptr, offset)
+}
+
+func (val *Node) next() {
+	*val = NewNode(val.Next())
+}
+
+type NodeIter struct {
+	next uintptr
+}
+
+func NewNodeIter(node Node) NodeIter {
+	return NodeIter{next: node.cptr}
+}
+
+func (iter *NodeIter) Next() Node {
+	ret := NewNode(iter.next)
+	iter.next = PtrOffset(iter.next, 1)
+	return ret
+}
+
+
+func (iter *NodeIter) Peek() Node {
+	return NewNode(iter.next)
+}
+
+func (val Node) U64() uint64 {
+	cnum := ptrCast(val.cptr)
+	return *(*uint64)((unsafe.Pointer)(&(cnum.val)))
+}
+
+func (val Node) I64() int64 {
+	cnum := ptrCast(val.cptr)
+	return *(*int64)((unsafe.Pointer)(&(cnum.val)))
+}
+
+func (val Node) IsNull() bool {
+	return val.Type() == KNull
+}
+
+func (val Node) IsNumber() bool {
+	return val.Type() & KNumber != 0
+}
+
+func (val Node) F64() float64 {
+	cnum := ptrCast(val.cptr)
+	return *(*float64)((unsafe.Pointer)(&(cnum.val)))
+}
+
+func (val Node) Bool() bool {
+	return val.Type() == KTrue
+}
+
+func (self Node) AsU64(ctx *Context) (uint64, bool) {
+	if self.Type() == KUint {
+		return self.U64(), true
+	} else if self.Type() == KRawNumber {
+		num, err := ParseU64(self.Raw(ctx))
+		if err != nil {
+			return 0, false
+		}
+		return num, true
+	} else {
+		return 0, false
+	}
+}
+
+func (val *Node) AsObj() (Object, bool) {
+	var ret Object
+	if val.Type() != KObject {
+		return ret, false
+	}
+	return Object{
+		cptr: val.cptr,
+	}, true
+}
+
+func (val Node) Obj() Object {
+	return Object{cptr: val.cptr}
+}
+
+func (val Node) Arr() Array {
+	return Array{cptr: val.cptr}
+}
+
+func (val *Node) AsArr() (Array, bool) {
+	var ret Array
+	if val.Type() != KArray {
+		return ret, false
+	}
+	return Array{
+		cptr: val.cptr,
+	}, true
+}
+
+func (self Node) AsI64(ctx *Context) (int64, bool) {
+	typ := self.Type()
+	if typ == KUint && self.U64() <= math.MaxInt64 {
+		return int64(self.U64()), true
+	} else  if typ == KSint {
+		return self.I64(), true
+	} else if typ == KRawNumber {
+		val, err := self.Number(ctx).Int64()
+		if err != nil {
+			return 0, false
+		}
+		return val, true
+	} else {
+		return 0, false
+	}
+}
+
+func (self Node) AsByte(ctx *Context) (uint8, bool) {
+	typ := self.Type()
+	if typ == KUint && self.U64() <= math.MaxUint8 {
+		return uint8(self.U64()), true
+	} else if typ == KSint && self.I64() == 0 {
+		return 0, true
+	} else {
+		return 0, false
+	}
+}
+
+/********* Parse Node String into Value ***************/
+
+func (val Node) ParseI64(ctx *Context) (int64, bool) {
+	s, ok := val.AsStrRef(ctx)
+	if !ok {
+		return 0, false
+	}
+
+	if s == "null" {
+		return 0, true
+	}
+
+	i, err := ParseI64(s)
+	if err != nil {
+		return 0, false
+	}
+	return i, true
+}
+
+func (val Node) ParseBool(ctx *Context) (bool, bool) {
+	s, ok := val.AsStrRef(ctx)
+	if !ok {
+		return false, false
+	}
+
+	if s == "null" {
+		return false, true
+	}
+
+	b, err := ParseBool(s)
+	if err != nil {
+		return false, false
+	}
+	return b, true
+}
+
+func (val Node) ParseU64(ctx *Context) (uint64, bool) {
+	s, ok := val.AsStrRef(ctx)
+	if !ok {
+		return 0, false
+	}
+
+	if s == "null" {
+		return 0, true
+	}
+
+	i, err := ParseU64(s)
+	if err != nil {
+		return 0, false
+	}
+	return i, true
+}
+
+func (val Node) ParseF64(ctx *Context) (float64, bool) {
+	s, ok := val.AsStrRef(ctx)
+	if !ok {
+		return 0, false
+	}
+
+	if s == "null" {
+		return 0, true
+	}
+
+	i, err := ParseF64(s)
+	if err != nil {
+		return 0, false
+	}
+	return i, true
+}
+
+func (val Node) ParseString(ctx *Context) (string, bool) {
+	// should not use AsStrRef
+	s, ok := val.AsStr(ctx)
+	if !ok {
+		return "", false
+	}
+
+	if s == "null" {
+		return "", true
+	}
+
+	s, err := Unquote(s)
+	if err != nil {
+		return "", false
+	}
+	return s, true
+}
+
+
+func (val Node) ParseNumber(ctx *Context) (json.Number, bool) {
+	// should not use AsStrRef
+	s, ok := val.AsStr(ctx)
+	if !ok {
+		return json.Number(""), false
+	}
+
+	if s == "null" {
+		return json.Number(""), true
+	}
+
+	end, ok := SkipNumberFast(s, 0)
+	// has error or trailing chars
+	if !ok || end != len(s) {
+		return json.Number(""),  false
+	}
+	return json.Number(s), true
+}
+
+
+
+func (val Node) AsF64(ctx *Context) (float64, bool) {
+	switch val.Type() {
+		case KUint: return float64(val.U64()), true
+		case KSint: return float64(val.I64()), true
+		case KReal: return float64(val.F64()), true
+		case KRawNumber: f, err := val.Number(ctx).Float64(); return f, err == nil
+		default: return 0, false
+	}
+}
+
+func (val Node) AsBool() (bool, bool) {
+	switch val.Type() {
+		case KTrue: return true, true
+		case KFalse: return false, true
+		default: return false, false
+	}
+}
+
+func (val Node) AsStr(ctx *Context) (string, bool) {
+	switch val.Type() {
+		case KStringCommon:
+			s := val.StringRef(ctx)
+			if (ctx.Options() & (1 << _F_copy_string) == 0) {
+				return s, true
+			}
+			return string(rt.Str2Mem(s)), true
+		case KStringEscaped:
+			return val.StringCopyEsc(ctx), true
+		default: return "", false
+	}
+}
+
+func (val Node) AsStrRef(ctx *Context) (string, bool) {
+	switch val.Type() {
+	case KStringEscaped:
+		node := ptrCast(val.cptr)
+		offset := val.Position()
+		len := int(node.val)
+		return rt.Mem2Str(ctx.Parser.JsonBytes()[offset : offset + len]), true
+	case KStringCommon:
+		return val.StringRef(ctx), true
+	default:
+		return "", false
+	}
+}
+
+func (val Node) AsStringText(ctx *Context) ([]byte, bool) {
+	if !val.IsStr() {
+		return nil, false
+	}
+
+	// clone to new bytes
+	s, b := val.AsStrRef(ctx)
+	return []byte(s), b
+}
+
+func (val Node) IsStr() bool {
+	return (val.Type() == KStringCommon) || (val.Type() == KStringEscaped)
+}
+
+func (val Node) IsRawNumber() bool {
+	return val.Type() == KRawNumber
+}
+
+func (val Node) Number(ctx *Context) json.Number {
+	return json.Number(val.Raw(ctx))
+}
+
+func (val Node) Raw(ctx *Context) string {
+	node := ptrCast(val.cptr)
+	len := int(node.val)
+	offset := val.Position()
+	return ctx.Parser.Json[offset:int(offset+len)]
+}
+
+func (val Node) Position() int {
+	node := ptrCast(val.cptr)
+	return int(node.typ >> PosBits)
+}
+
+func (val Node) AsNumber(ctx *Context) (json.Number, bool) {
+	// parse JSON string as number
+	if val.IsStr() {
+		s, _ := val.AsStr(ctx)
+		if !ValidNumberFast(s) {
+			return "", false
+		} else {
+			return json.Number(s), true
+		}
+	}
+
+	return val.NonstrAsNumber(ctx)
+}
+
+func (val Node) NonstrAsNumber(ctx *Context) (json.Number, bool) {
+	// deal with raw number
+	if val.IsRawNumber() {
+		return val.Number(ctx), true
+	}
+
+	// deal with parse number
+	if !val.IsNumber() {
+		return json.Number(""), false
+	}
+
+	start := val.Position()
+	end, ok := SkipNumberFast(ctx.Parser.Json, start)
+	if !ok {
+		return "", false
+	}
+	return json.Number(ctx.Parser.Json[start:end]), true
+}
+
+func (val Node) AsRaw(ctx *Context) string {
+	// fast path for unescaped strings
+	switch val.Type() {
+	case KNull:
+		return "null"
+	case KTrue:
+		return "true"
+	case KFalse:
+		return "false"
+	case KStringCommon:
+		node := ptrCast(val.cptr)
+		len := int(node.val)
+		offset := val.Position()
+		// add start and end quote
+		ref := rt.Str2Mem(ctx.Parser.Json)[offset-1 : offset+len+1]
+		return rt.Mem2Str(ref)
+	case KRawNumber: fallthrough
+	case KRaw: return val.Raw(ctx)
+	case KStringEscaped:
+		raw, _ := SkipOneFast(ctx.Parser.Json, val.Position() - 1)
+		return raw
+	default:
+		raw, err := SkipOneFast(ctx.Parser.Json, val.Position())
+		if err != nil {
+			break
+		}
+		return raw
+	}
+	panic("should always be valid json here")
+}
+
+// reference from the input JSON as possible
+func (val Node) StringRef(ctx *Context) string {
+	return val.Raw(ctx)
+}
+
+//go:nocheckptr
+func ptrCast(p uintptr) *node {
+	return (*node)(unsafe.Pointer(p))
+}
+
+func (val Node) StringCopyEsc(ctx *Context) string {
+	// check whether there are in padded
+	node := ptrCast(val.cptr)
+	len := int(node.val)
+	offset := val.Position()
+	return string(ctx.Parser.JsonBytes()[offset : offset + len])
+}
+
+func (val Node) Object() Object {
+	return Object{cptr: val.cptr}
+}
+
+func (val Node) Array() Array {
+	return Array{cptr: val.cptr}
+}
+
+func (val *Array) Children() uintptr {
+	return PtrOffset(val.cptr, 1)
+}
+
+func (val *Object) Children() uintptr {
+	return PtrOffset(val.cptr, 1)
+}
+
+func (val *Node) Equal(ctx *Context, lhs string) bool {
+	// check whether escaped
+	cstr := ptrCast(val.cptr)
+	offset := int(val.Position())
+	len := int(cstr.val)
+	return lhs == ctx.Parser.Json[offset:offset+len]
+}
+
+func (node *Node) AsMapEface(ctx *Context, vp unsafe.Pointer) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	obj, ok := node.AsObj()
+	if !ok {
+		return newUnmatched(node.Position(), rt.MapEfaceType)
+	}
+
+	var err, gerr error
+	size := obj.Len()
+
+	var m map[string]interface{}
+	if *(*unsafe.Pointer)(vp) == nil {
+		if ctx.efacePool != nil {
+			p := ctx.efacePool.GetMap(size)
+			m = *(*map[string]interface{})(unsafe.Pointer(&p))
+		} else {
+			m = make(map[string]interface{}, size)
+		}
+	} else {
+		m = *(*map[string]interface{})(vp)
+	}
+
+	next := obj.Children()
+	for i := 0; i < size; i++ {
+		knode := NewNode(next)
+		key, _ := knode.AsStr(ctx)
+		val := NewNode(PtrOffset(next, 1))
+		m[key], err = val.AsEface(ctx)
+		next = val.cptr
+		if gerr == nil && err != nil {
+			gerr = err
+		}
+	}
+
+	*(*map[string]interface{})(vp) = m
+	return gerr
+}
+
+func (node *Node) AsMapString(ctx *Context, vp unsafe.Pointer) error {
+	obj, ok := node.AsObj()
+	if !ok {
+		return newUnmatched(node.Position(), rt.MapStringType)
+	}
+
+	size := obj.Len()
+
+	var m map[string]string
+	if *(*unsafe.Pointer)(vp) == nil {
+		m = make(map[string]string, size)
+	} else {
+		m = *(*map[string]string)(vp)
+	}
+
+	var gerr error
+	next := obj.Children()
+	for i := 0; i < size; i++ {
+		knode := NewNode(next)
+		key, _ := knode.AsStr(ctx)
+		val := NewNode(PtrOffset(next, 1))
+		m[key], ok = val.AsStr(ctx)
+		if !ok {
+			if gerr == nil {
+				gerr = newUnmatched(val.Position(), rt.StringType)
+			}
+			next = val.Next()
+		} else {
+			next = PtrOffset(val.cptr, 1)
+		}
+	}
+
+	*(*map[string]string)(vp) = m
+	return gerr
+}
+
+func (node *Node) AsSliceEface(ctx *Context, vp unsafe.Pointer) error {
+	arr, ok := node.AsArr()
+	if !ok {
+		return newUnmatched(node.Position(), rt.SliceEfaceType)
+	}
+
+	size := arr.Len()
+	var s []interface{}
+	if size != 0 && ctx.efacePool != nil {
+		slice := rt.GoSlice {
+			Ptr: ctx.efacePool.GetSlice(size),
+			Len: size,
+			Cap: size,
+		}
+		*(*rt.GoSlice)(unsafe.Pointer(&s)) = slice
+	} else {
+		s = *(*[]interface{})((unsafe.Pointer)(rt.MakeSlice(vp, rt.AnyType, size)))
+	}
+
+	*node = NewNode(arr.Children())
+
+	var err, gerr error
+	for i := 0; i < size; i++ {
+		s[i], err = node.AsEface(ctx)
+		if gerr == nil && err != nil {
+			gerr = err
+		}
+	}
+
+	*(*[]interface{})(vp) = s
+	return nil
+}
+
+func (node *Node) AsSliceI32(ctx *Context, vp unsafe.Pointer) error {
+	arr, ok := node.AsArr()
+	if !ok {
+		return newUnmatched(node.Position(), rt.SliceI32Type)
+	}
+
+	size := arr.Len()
+	s := *(*[]int32)((unsafe.Pointer)(rt.MakeSlice(vp, rt.Int32Type, size)))
+	next := arr.Children()
+
+	var gerr error
+	for i := 0; i < size; i++ {
+		val := NewNode(next)
+		ret, ok := val.AsI64(ctx)
+		if !ok || ret > math.MaxInt32 || ret < math.MinInt32 {
+			if gerr == nil {
+				gerr = newUnmatched(val.Position(), rt.Int32Type)
+			}
+			next = val.Next()
+		} else {
+			s[i] = int32(ret)
+			next = PtrOffset(val.cptr, 1)
+		}
+	}
+
+	*(*[]int32)(vp) = s
+	return gerr
+}
+
+func (node *Node) AsSliceI64(ctx *Context, vp unsafe.Pointer) error {
+	arr, ok := node.AsArr()
+	if !ok {
+		return newUnmatched(node.Position(), rt.SliceI64Type)
+	}
+
+	size := arr.Len()
+	s := *(*[]int64)((unsafe.Pointer)(rt.MakeSlice(vp, rt.Int64Type, size)))
+	next := arr.Children()
+
+	var gerr error
+	for i := 0; i < size; i++ {
+		val := NewNode(next)
+
+		ret, ok := val.AsI64(ctx)
+		if !ok {
+			if gerr == nil {
+				gerr = newUnmatched(val.Position(), rt.Int64Type)
+			}
+			next = val.Next()
+		} else {
+			s[i] = ret
+			next = PtrOffset(val.cptr, 1)
+		}
+	}
+
+	*(*[]int64)(vp) = s
+	return gerr
+}
+
+func (node *Node) AsSliceU32(ctx *Context, vp unsafe.Pointer) error {
+	arr, ok := node.AsArr()
+	if !ok {
+		return newUnmatched(node.Position(), rt.SliceU32Type)
+	}
+
+	size := arr.Len()
+	next := arr.Children()
+	s := *(*[]uint32)((unsafe.Pointer)(rt.MakeSlice(vp, rt.Uint32Type, size)))
+
+	var gerr error
+	for i := 0; i < size; i++ {
+		val := NewNode(next)
+		ret, ok := val.AsU64(ctx)
+		if !ok ||  ret > math.MaxUint32 {
+			if gerr == nil {
+				gerr = newUnmatched(val.Position(), rt.Uint32Type)
+			}
+			next = val.Next()
+		} else {
+			s[i] = uint32(ret)
+			next = PtrOffset(val.cptr, 1)
+		}
+	}
+
+	*(*[]uint32)(vp) = s
+	return gerr
+}
+
+func (node *Node) AsSliceU64(ctx *Context, vp unsafe.Pointer) error {
+	arr, ok := node.AsArr()
+	if !ok {
+		return newUnmatched(node.Position(), rt.SliceU64Type)
+	}
+
+	size := arr.Len()
+	next := arr.Children()
+
+	s := *(*[]uint64)((unsafe.Pointer)(rt.MakeSlice(vp, rt.Uint64Type, size)))
+	var gerr error
+	for i := 0; i < size; i++ {
+		val := NewNode(next)
+		ret, ok := val.AsU64(ctx)
+		if !ok {
+			if gerr == nil {
+				gerr = newUnmatched(val.Position(), rt.Uint64Type)
+			}
+			next = val.Next()
+		} else {
+			s[i] = ret
+			next = PtrOffset(val.cptr, 1)
+		}
+	}
+
+	*(*[]uint64)(vp) = s
+	return gerr
+}
+
+func (node *Node) AsSliceString(ctx *Context, vp unsafe.Pointer) error {
+	arr, ok := node.AsArr()
+	if !ok {
+		return newUnmatched(node.Position(), rt.SliceStringType)
+	}
+
+	size := arr.Len()
+	next := arr.Children()
+	s := *(*[]string)((unsafe.Pointer)(rt.MakeSlice(vp, rt.StringType, size)))
+
+	var gerr error
+	for i := 0; i < size; i++ {
+		val := NewNode(next)
+		ret, ok := val.AsStr(ctx)
+		if !ok {
+			if gerr == nil {
+				gerr = newUnmatched(val.Position(), rt.StringType)
+			}
+			next = val.Next()
+		} else {
+			s[i] = ret
+			next = PtrOffset(val.cptr, 1)
+		}
+	}
+
+	*(*[]string)(vp) = s
+	return gerr
+}
+
+func (val *Node) AsSliceBytes(ctx *Context) ([]byte, error) {
+	var origin []byte
+	switch val.Type() {
+	case KStringEscaped:
+		node := ptrCast(val.cptr)
+		offset := val.Position()
+		len := int(node.val)
+		origin = ctx.Parser.JsonBytes()[offset : offset + len]
+	case KStringCommon:
+		origin = rt.Str2Mem(val.StringRef(ctx))
+	case KArray:
+		arr := val.Array()
+		size := arr.Len()
+		a := make([]byte, size)
+		elem := NewNode(arr.Children())
+		var gerr error
+		var ok bool
+		for i := 0; i < size; i++ {
+			a[i], ok = elem.AsByte(ctx)
+			if !ok && gerr == nil {
+				gerr = newUnmatched(val.Position(), rt.BytesType)
+			}
+			elem = NewNode(PtrOffset(elem.cptr, 1))
+		}
+		return a, gerr
+	default:
+		return nil,  newUnmatched(val.Position(), rt.BytesType)
+	}
+	
+	b64, err := rt.DecodeBase64(origin)
+	if err != nil {
+		return nil, newUnmatched(val.Position(), rt.BytesType)
+	}
+	return b64, nil
+}
+
+// AsEface will always ok, because we have parse in native.
+func (node *Node) AsEface(ctx *Context) (interface{}, error) {
+	if ctx.efacePool != nil {
+		iter := NewNodeIter(*node)
+		v := AsEfaceFast(&iter, ctx)
+		*node = iter.Peek()
+		return v, nil
+	} else {
+		return node.AsEfaceFallback(ctx)
+	}
+}
+
+func parseSingleNode(node Node, ctx *Context) interface{} {
+	var v interface{}
+	switch node.Type() {
+		case KObject: 			v = map[string]interface{}{}
+		case KArray: 			v = []interface{}{}
+		case KStringCommon: 	v = node.StringRef(ctx)
+		case KStringEscaped:	v = node.StringCopyEsc(ctx)
+		case KTrue:				v = true
+		case KFalse:			v = false
+		case KNull:				v = nil
+		case KUint:				v = float64(node.U64())
+		case KSint: 			v = float64(node.I64())
+		case KReal:				v = float64(node.F64())
+		case KRawNumber:		v = node.Number(ctx)
+		default:				panic("unreachable for as eface")
+	}
+	return v
+}
+
+func castU64(val float64) uint64 {
+	return *((*uint64)(unsafe.Pointer((&val))))
+}
+
+func AsEfaceFast(iter *NodeIter, ctx *Context) interface{} {
+	var mp, sp, parent unsafe.Pointer // current container pointer
+	var node Node
+	var size int
+	var isObj bool
+	var slice rt.GoSlice
+	var val unsafe.Pointer
+	var vt **rt.GoType
+	var vp *unsafe.Pointer
+	var rootM unsafe.Pointer
+	var rootS rt.GoSlice
+	var root interface{}
+	var key string
+
+	node = iter.Next()
+
+	switch node.Type() {
+	case KObject: 
+		size = node.Object().Len()
+		if size != 0 {
+			ctx.Stack.Push(nil, 0, true)
+			mp = ctx.efacePool.GetMap(size)
+			rootM = mp
+			isObj = true
+			goto _object_key
+		} else {
+			return rt.GoEface {
+				Type: rt.MapEfaceType,
+				Value: ctx.efacePool.GetMap(0),
+			}.Pack()
+		}
+	case KArray:
+		size = node.Array().Len()
+		if size != 0 {
+			ctx.Stack.Push(nil, 0, false)
+			sp = ctx.efacePool.GetSlice(size)
+			slice = rt.GoSlice {
+				Ptr: sp,
+				Len: size,
+				Cap: size,
+			}
+			rootS = slice
+			isObj = false
+			val = sp
+			goto _arr_val;
+		} else {
+			ctx.efacePool.ConvTSlice(rt.EmptySlice, rt.SliceEfaceType, unsafe.Pointer(&root))
+		}
+	case KStringCommon: 	ctx.efacePool.ConvTstring(node.StringRef(ctx), unsafe.Pointer(&root))
+	case KStringEscaped:	ctx.efacePool.ConvTstring(node.StringCopyEsc(ctx), unsafe.Pointer(&root))  
+	case KTrue:				root = true
+	case KFalse:			root = false
+	case KNull:				root = nil
+	case KUint:				ctx.efacePool.ConvF64(float64(node.U64()), unsafe.Pointer(&root))  
+	case KSint: 			ctx.efacePool.ConvF64(float64(node.I64()), unsafe.Pointer(&root))
+	case KReal:				ctx.efacePool.ConvF64(node.F64(), unsafe.Pointer(&root))
+	case KRawNumber:		ctx.efacePool.ConvTnum(node.Number(ctx), unsafe.Pointer(&root))
+	default:				panic("unreachable for as eface")
+	}
+	return root
+
+_object_key:
+	node = iter.Next()
+	if  node.Type() ==  KStringCommon {
+		key = node.StringRef(ctx)
+	} else {
+		key = node.StringCopyEsc(ctx)
+	}
+
+	// interface{} slot in map bucket
+	val = rt.Mapassign_faststr(rt.MapEfaceMapType, mp, key)
+	vt = &(*rt.GoEface)(val).Type
+	vp = &(*rt.GoEface)(val).Value
+
+	// parse value node
+	node = iter.Next()
+	switch node.Type() {
+		case KObject:
+			newSize := node.Object().Len()
+			newMp := ctx.efacePool.GetMap(newSize)
+			*vt = rt.MapEfaceType
+			*vp = newMp
+			remain := size - 1
+			isObj = true
+			if newSize != 0 {
+				if remain > 0 {
+					ctx.Stack.Push(mp, remain, true)
+				}
+				mp = newMp
+				size = newSize
+				goto _object_key;
+			}
+		case KArray:
+			newSize := node.Array().Len()
+			if newSize == 0 {
+				ctx.efacePool.ConvTSlice(rt.EmptySlice, rt.SliceEfaceType, val)
+				break;
+			}
+
+			newSp := ctx.efacePool.GetSlice(newSize)
+			// pack to []interface{}
+			ctx.efacePool.ConvTSlice(rt.GoSlice{
+				Ptr: newSp,
+				Len: newSize,
+				Cap: newSize,
+			}, rt.SliceEfaceType, val)
+			remain := size - 1
+			if remain > 0 {
+				ctx.Stack.Push(mp, remain, true)
+			}
+			val = newSp
+			isObj = false
+			size = newSize
+			goto _arr_val;
+		case KStringCommon:
+			ctx.efacePool.ConvTstring(node.StringRef(ctx), val)
+		case KStringEscaped:
+			ctx.efacePool.ConvTstring(node.StringCopyEsc(ctx), val)
+		case KTrue:
+			rt.ConvTBool(true, (*interface{})(val))
+		case KFalse:
+			rt.ConvTBool(false, (*interface{})(val))
+		case KNull: /* skip */
+		case KUint:
+			ctx.efacePool.ConvF64(float64(node.U64()), val)
+		case KSint: 
+			ctx.efacePool.ConvF64(float64(node.I64()), val)
+		case KReal: 
+			ctx.efacePool.ConvF64(node.F64(), val)
+		case KRawNumber:
+			ctx.efacePool.ConvTnum(node.Number(ctx), val)
+		default: 
+			panic("unreachable for as eface")
+	}
+	
+	// check size 
+	size -= 1
+	if size != 0 {
+		goto _object_key;
+	}
+
+	parent, size, isObj = ctx.Stack.Pop()
+
+	// parent is empty
+	if parent == nil {
+		if isObj {
+			return rt.GoEface {
+				Type: rt.MapEfaceType,
+				Value: rootM,
+			}.Pack()
+		} else {
+			ctx.efacePool.ConvTSlice(rootS, rt.SliceEfaceType, (unsafe.Pointer)(&root))
+			return root
+		}
+	}
+
+	// continue to parse parent
+	if isObj {
+		mp = parent
+		goto _object_key;
+	} else {
+		val = rt.PtrAdd(parent, rt.AnyType.Size)
+		goto _arr_val;
+	}
+
+_arr_val:
+	// interface{} slot in slice
+	vt = &(*rt.GoEface)(val).Type
+	vp = &(*rt.GoEface)(val).Value
+
+	// parse value node
+	node = iter.Next()
+	switch node.Type() {
+		case KObject:
+			newSize := node.Object().Len()
+			newMp := ctx.efacePool.GetMap(newSize)
+			*vt = rt.MapEfaceType
+			*vp = newMp
+			remain := size - 1
+			if newSize != 0 {
+				// push next array elem into stack
+				if remain > 0 {
+					ctx.Stack.Push(val, remain, false)
+				}
+				mp = newMp
+				size = newSize
+				isObj = true
+				goto _object_key;
+			}
+		case KArray:
+			newSize := node.Array().Len()
+			if newSize == 0 {
+				ctx.efacePool.ConvTSlice(rt.EmptySlice, rt.SliceEfaceType, val)
+				break;
+			}
+			
+			newSp := ctx.efacePool.GetSlice(newSize)
+			// pack to []interface{}
+			ctx.efacePool.ConvTSlice(rt.GoSlice {
+				Ptr: newSp,
+				Len: newSize,
+				Cap: newSize,
+			}, rt.SliceEfaceType, val)
+
+			remain := size - 1
+			if remain > 0 {
+				ctx.Stack.Push(val, remain, false)
+			}
+
+			val = newSp
+			isObj = false
+			size = newSize
+			goto _arr_val;
+		case KStringCommon:
+			ctx.efacePool.ConvTstring(node.StringRef(ctx), val)
+		case KStringEscaped:
+			ctx.efacePool.ConvTstring(node.StringCopyEsc(ctx), val)
+		case KTrue:
+			rt.ConvTBool(true, (*interface{})(val))
+		case KFalse:
+			rt.ConvTBool(false, (*interface{})(val))
+		case KNull: /* skip */
+		case KUint:
+			ctx.efacePool.ConvF64(float64(node.U64()), val)
+		case KSint: 
+			ctx.efacePool.ConvF64(float64(node.I64()), val)
+		case KReal: 
+			ctx.efacePool.ConvF64(node.F64(), val)
+		case KRawNumber:
+			ctx.efacePool.ConvTnum(node.Number(ctx), val)
+		default: panic("unreachable for as eface")
+	}
+
+	// check size 
+	size -= 1
+	if size != 0 {
+		val = rt.PtrAdd(val, rt.AnyType.Size)
+		goto _arr_val;
+	}
+
+
+	parent, size, isObj = ctx.Stack.Pop()
+
+	// parent is empty
+	if parent == nil {
+		if isObj {
+			return rt.GoEface {
+				Type: rt.MapEfaceType,
+				Value: rootM,
+			}.Pack()
+		} else {
+			ctx.efacePool.ConvTSlice(rootS, rt.SliceEfaceType, unsafe.Pointer(&root))
+			return root
+		}
+	}
+
+	// continue to parse parent
+	if isObj {
+		mp = parent
+		goto _object_key;
+	} else {
+		val = rt.PtrAdd(parent, rt.AnyType.Size)
+		goto _arr_val;
+	}
+}
+
+func (node *Node) AsEfaceFallback(ctx *Context) (interface{}, error) {
+	switch node.Type() {
+	case KObject:
+		obj := node.Object()
+		size := obj.Len()
+		m := make(map[string]interface{}, size)
+		*node = NewNode(obj.Children())
+		var gerr, err error
+		for i := 0; i < size; i++ {
+			key, _ := node.AsStr(ctx)
+			*node = NewNode(PtrOffset(node.cptr, 1))
+			m[key], err = node.AsEfaceFallback(ctx)
+			if gerr == nil && err != nil {
+				gerr = err
+			}
+		}
+		return m, gerr
+	case KArray:
+		arr := node.Array()
+		size := arr.Len()
+		a := make([]interface{}, size)
+		*node = NewNode(arr.Children())
+		var gerr, err error
+		for i := 0; i < size; i++ {
+			a[i], err = node.AsEfaceFallback(ctx)
+			if gerr == nil && err != nil {
+				gerr = err
+			}
+		}
+		return a, gerr
+	case KStringCommon:
+		str, _ := node.AsStr(ctx)
+		*node = NewNode(PtrOffset(node.cptr, 1))
+		return str, nil
+	case KStringEscaped:
+		str := node.StringCopyEsc(ctx)
+		*node = NewNode(PtrOffset(node.cptr, 1))
+		return str, nil
+	case KTrue:
+		*node = NewNode(PtrOffset(node.cptr, 1))
+		return true, nil
+	case KFalse:
+		*node = NewNode(PtrOffset(node.cptr, 1))
+		return false, nil
+	case KNull:
+		*node = NewNode(PtrOffset(node.cptr, 1))
+		return nil, nil
+	default:
+		// use float64
+		if ctx.Parser.options & (1 << _F_use_number) != 0 {
+			num, ok := node.AsNumber(ctx)
+			if !ok {
+				// skip the unmacthed type
+				*node = NewNode(node.Next())
+				return nil, newUnmatched(node.Position(), rt.JsonNumberType)
+			} else {
+				*node = NewNode(PtrOffset(node.cptr, 1))
+				return num, nil
+			}
+		} else if  ctx.Parser.options & (1 << _F_use_int64) != 0 {
+			// first try int64
+			i, ok := node.AsI64(ctx)
+			if ok {
+				*node = NewNode(PtrOffset(node.cptr, 1))
+				return i, nil
+			}
+
+			// is not integer, then use float64
+			f, ok := node.AsF64(ctx)
+			if ok {
+				*node = NewNode(PtrOffset(node.cptr, 1))
+				return f, nil
+			}
+		
+			// skip the unmacthed type
+			*node = NewNode(node.Next())
+			return nil, newUnmatched(node.Position(), rt.Int64Type)
+		} else {
+			num, ok := node.AsF64(ctx)
+			if !ok {
+				// skip the unmacthed type
+				*node = NewNode(node.Next())
+				return nil, newUnmatched(node.Position(), rt.Float64Type)
+			} else {
+				*node = NewNode(PtrOffset(node.cptr, 1))
+				return num, nil
+			}
+		}
+	}
+}
+
+//go:nosplit
+func PtrOffset(ptr uintptr, off int64) uintptr {
+	return uintptr(int64(ptr) + off * int64(unsafe.Sizeof(node{})))
+}

+ 235 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/slice.go

@@ -0,0 +1,235 @@
+package optdec
+
+import (
+	"reflect"
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/rt"
+)
+
+type sliceDecoder struct {
+	elemType *rt.GoType
+	elemDec  decFunc
+	typ      reflect.Type
+}
+
+var (
+	emptyPtr = &struct{}{}
+)
+
+func (d *sliceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*rt.GoSlice)(vp) = rt.GoSlice{}
+		return nil
+	}
+
+	arr, ok := node.AsArr()
+	if !ok {
+		return error_mismatch(node, ctx, d.typ)
+	}
+
+	slice := rt.MakeSlice(vp, d.elemType, arr.Len())
+	elems := slice.Ptr
+	next := arr.Children()
+
+	var gerr error
+	for i := 0; i < arr.Len(); i++ {
+		val := NewNode(next)
+		elem := unsafe.Pointer(uintptr(elems) + uintptr(i)*d.elemType.Size)
+		err := d.elemDec.FromDom(elem, val, ctx)
+		if gerr == nil && err != nil {
+			gerr = err
+		}
+		next = val.Next()
+	}
+
+	*(*rt.GoSlice)(vp) = *slice
+	return gerr
+}
+
+type arrayDecoder struct {
+	len      int
+	elemType *rt.GoType
+	elemDec  decFunc
+	typ   	reflect.Type
+}
+
+//go:nocheckptr
+func (d *arrayDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	arr, ok := node.AsArr()
+	if !ok {
+		return error_mismatch(node, ctx, d.typ)
+	}
+
+	next := arr.Children()
+	i := 0
+
+	var gerr error
+	for ; i < d.len && i < arr.Len(); i++ {
+		elem := unsafe.Pointer(uintptr(vp) + uintptr(i)*d.elemType.Size)
+		val := NewNode(next)
+		err := d.elemDec.FromDom(elem, val, ctx)
+		if gerr == nil && err != nil {
+			gerr = err
+		}
+		next = val.Next()
+	}
+
+	/* zero rest of array */
+	addr := uintptr(vp) + uintptr(i)*d.elemType.Size
+	n := uintptr(d.len-i) * d.elemType.Size
+
+	/* the boundary pointer may points to another unknown object, so we need to avoid using it */
+	if n != 0 {
+		rt.ClearMemory(d.elemType, unsafe.Pointer(addr), n)
+	}
+	return gerr
+}
+
+type sliceEfaceDecoder struct {
+}
+
+func (d *sliceEfaceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*rt.GoSlice)(vp) = rt.GoSlice{}
+		return nil
+	}
+
+	/* if slice is empty, just call `AsSliceEface` */
+	if ((*rt.GoSlice)(vp)).Len == 0 {
+		return node.AsSliceEface(ctx, vp)
+	}
+	
+	decoder := sliceDecoder{
+		elemType: rt.AnyType,
+		elemDec:  &efaceDecoder{},
+		typ:      rt.SliceEfaceType.Pack(),
+	}
+
+	return decoder.FromDom(vp, node, ctx)
+}
+
+type sliceI32Decoder struct {
+}
+
+func (d *sliceI32Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*rt.GoSlice)(vp) = rt.GoSlice{}
+		return nil
+	}
+
+	return node.AsSliceI32(ctx, vp)
+}
+
+type sliceI64Decoder struct {
+}
+
+func (d *sliceI64Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*rt.GoSlice)(vp) = rt.GoSlice{}
+		return nil
+	}
+
+	return node.AsSliceI64(ctx, vp)
+}
+
+type sliceU32Decoder struct {
+}
+
+func (d *sliceU32Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*rt.GoSlice)(vp) = rt.GoSlice{}
+		return nil
+	}
+
+	return node.AsSliceU32(ctx, vp)
+}
+
+type sliceU64Decoder struct {
+}
+
+func (d *sliceU64Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*rt.GoSlice)(vp) = rt.GoSlice{}
+		return nil
+	}
+
+	return node.AsSliceU64(ctx, vp)
+}
+
+type sliceStringDecoder struct {
+}
+
+func (d *sliceStringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*rt.GoSlice)(vp) = rt.GoSlice{}
+		return nil
+	}
+
+	return node.AsSliceString(ctx, vp)
+}
+
+type sliceBytesDecoder struct {
+}
+
+func (d *sliceBytesDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*rt.GoSlice)(vp) = rt.GoSlice{}
+		return nil
+	}
+
+	s, err := node.AsSliceBytes(ctx)
+	*(*[]byte)(vp) = s
+	return err
+}
+
+type sliceBytesUnmarshalerDecoder struct {
+	elemType *rt.GoType
+	elemDec  decFunc
+	typ reflect.Type
+}
+
+func (d *sliceBytesUnmarshalerDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*rt.GoSlice)(vp) = rt.GoSlice{}
+		return nil
+	}
+
+	/* parse JSON string into `[]byte` */
+	if node.IsStr() {
+		slice, err := node.AsSliceBytes(ctx)
+		if err != nil {
+			return err
+		}
+		*(*[]byte)(vp) = slice
+		return nil
+	}
+
+	/* parse JSON array into `[]byte` */
+	arr, ok := node.AsArr()
+	if !ok {
+		return error_mismatch(node, ctx, d.typ)
+	}
+
+	slice := rt.MakeSlice(vp, d.elemType, arr.Len())
+	elems := slice.Ptr
+
+	var gerr error
+	next := arr.Children()
+	for i := 0; i < arr.Len(); i++ {
+		child := NewNode(next)
+		elem := unsafe.Pointer(uintptr(elems) + uintptr(i)*d.elemType.Size)
+		err := d.elemDec.FromDom(elem, child, ctx)
+		if gerr == nil && err != nil {
+			gerr = err
+		}
+		next = child.Next()
+	}
+
+	*(*rt.GoSlice)(vp) = *slice
+	return gerr
+}

+ 360 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/stringopts.go

@@ -0,0 +1,360 @@
+package optdec
+
+import (
+	"encoding/json"
+	"math"
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/rt"
+)
+
+type ptrStrDecoder struct {
+	typ   *rt.GoType
+	deref decFunc
+}
+
+// Pointer Value is allocated in the Caller
+func (d *ptrStrDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		*(*unsafe.Pointer)(vp) = nil
+		return nil
+	}
+
+	s, ok := node.AsStrRef(ctx)
+	if !ok {
+		return	error_mismatch(node, ctx, stringType)
+	}
+
+	if s == "null" {
+		*(*unsafe.Pointer)(vp) = nil
+		return nil
+	}
+
+	if *(*unsafe.Pointer)(vp) == nil {
+		*(*unsafe.Pointer)(vp) = rt.Mallocgc(d.typ.Size, d.typ, true)
+	}
+
+	return d.deref.FromDom(*(*unsafe.Pointer)(vp), node, ctx)
+}
+
+type boolStringDecoder struct {
+}
+
+func (d *boolStringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	s, ok := node.AsStrRef(ctx)
+	if !ok {
+		return error_mismatch(node, ctx, stringType)
+	}
+
+	if s == "null" {
+		return nil
+	}
+
+	b, err := ParseBool(s)
+	if err != nil {
+		return error_mismatch(node, ctx, boolType)
+	}
+
+	*(*bool)(vp) = b
+	return nil
+}
+
+func parseI64(node Node, ctx *context) (int64, error, bool) {
+	if node.IsNull() {
+		return 0, nil, true
+	}
+
+	s, ok := node.AsStrRef(ctx)
+	if !ok {
+		return 0, error_mismatch(node, ctx, stringType), false
+	}
+
+	if s == "null" {
+		return 0, nil, true
+	}
+
+	ret, err := ParseI64(s)
+	return ret, err, false
+}
+
+type i8StringDecoder struct{}
+
+func (d *i8StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	ret, err, null := parseI64(node, ctx)
+	if null {
+		return nil
+	}
+
+	if err != nil {
+		return err
+	}
+
+	if ret > math.MaxInt8 || ret < math.MinInt8 {
+		return error_mismatch(node, ctx, int8Type)
+	}
+
+	*(*int8)(vp) = int8(ret)
+	return nil
+}
+
+type i16StringDecoder struct{}
+
+func (d *i16StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	ret, err, null := parseI64(node, ctx)
+	if null {
+		return nil
+	}
+
+	if err != nil {
+		return err
+	}
+
+	if ret > math.MaxInt16 || ret < math.MinInt16 {
+		return error_mismatch(node, ctx, int16Type)
+	}
+
+	*(*int16)(vp) = int16(ret)
+	return nil
+}
+
+type i32StringDecoder struct{}
+
+func (d *i32StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	ret, err, null := parseI64(node, ctx)
+	if null {
+		return nil
+	}
+
+	if err != nil {
+		return err
+	}
+
+	if ret > math.MaxInt32 || ret < math.MinInt32 {
+		return error_mismatch(node, ctx, int32Type)
+	}
+
+	*(*int32)(vp) = int32(ret)
+	return nil
+}
+
+type i64StringDecoder struct{}
+
+func (d *i64StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	ret, err, null := parseI64(node, ctx)
+	if null {
+		return nil
+	}
+
+	if err != nil {
+		return err
+	}
+
+	*(*int64)(vp) = int64(ret)
+	return nil
+}
+
+func parseU64(node Node, ctx *context) (uint64, error, bool) {
+	if node.IsNull() {
+		return 0, nil, true
+	}
+
+	s, ok := node.AsStrRef(ctx)
+	if !ok {
+		return 0, error_mismatch(node, ctx, stringType), false
+	}
+
+	if s == "null" {
+		return 0, nil, true
+	}
+
+	ret, err := ParseU64(s)
+	return 	ret, err, false
+}
+
+type u8StringDecoder struct{}
+
+func (d *u8StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	ret, err, null := parseU64(node, ctx)
+	if null {
+		return nil
+	}
+
+	if err != nil {
+		return err
+	}
+
+	if ret > math.MaxUint8 {
+		return error_mismatch(node, ctx, uint8Type)
+	}
+
+	*(*uint8)(vp) = uint8(ret)
+	return nil
+}
+
+type u16StringDecoder struct{}
+
+func (d *u16StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	ret, err, null := parseU64(node, ctx)
+	if null {
+		return nil
+	}
+
+	if err != nil {
+		return err
+	}
+
+	if ret > math.MaxUint16 {
+		return error_mismatch(node, ctx, uint16Type)
+	}
+
+	*(*uint16)(vp) = uint16(ret)
+	return nil
+}
+
+type u32StringDecoder struct{}
+
+func (d *u32StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	ret, err, null := parseU64(node, ctx)
+	if null {
+		return nil
+	}
+
+	if err != nil {
+		return err
+	}
+
+	if ret > math.MaxUint32 {
+		return error_mismatch(node, ctx, uint32Type)
+	}
+
+	*(*uint32)(vp) = uint32(ret)
+	return nil
+}
+
+
+type u64StringDecoder struct{}
+
+func (d *u64StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	ret, err, null := parseU64(node, ctx)
+	if null {
+		return nil
+	}
+
+	if err != nil {
+		return err
+	}
+
+	*(*uint64)(vp) = uint64(ret)
+	return nil
+}
+
+type f32StringDecoder struct{}
+
+func (d *f32StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	s, ok := node.AsStrRef(ctx)
+	if !ok {
+		return error_mismatch(node, ctx, stringType)
+	}
+
+	if s == "null" {
+		return nil
+	}
+
+	ret, err := ParseF64(s)
+	if err != nil || ret > math.MaxFloat32 || ret < -math.MaxFloat32 {
+		return error_mismatch(node, ctx, float32Type)
+	}
+
+	*(*float32)(vp) = float32(ret)
+	return nil
+}
+
+type f64StringDecoder struct{}
+
+func (d *f64StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	s, ok := node.AsStrRef(ctx)
+	if !ok {
+		return error_mismatch(node, ctx, stringType)
+	}
+
+	if s == "null" {
+		return nil
+	}
+
+	ret, err := ParseF64(s)
+	if err != nil {
+		return error_mismatch(node, ctx, float64Type)
+	}
+
+	*(*float64)(vp) = float64(ret)
+	return nil
+}
+
+/* parse string field with string options */
+type strStringDecoder struct{}
+
+func (d *strStringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	s, ok := node.AsStrRef(ctx)
+	if !ok {
+		return error_mismatch(node, ctx, stringType)
+	}
+
+	if s == "null" {
+		return nil
+	}
+
+	s, err := Unquote(s)
+	if err != nil {
+		return error_mismatch(node, ctx, stringType)
+	}
+
+	*(*string)(vp) = s
+	return nil
+}
+
+type numberStringDecoder struct{}
+
+func (d *numberStringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	s, ok := node.AsStrRef(ctx)
+	if !ok {
+		return error_mismatch(node, ctx, stringType)
+	}
+
+	if s == "null" {
+		return nil
+	}
+
+	num, ok := node.ParseNumber(ctx)
+	if !ok {
+		return error_mismatch(node, ctx, jsonNumberType)
+	}
+
+	end, ok := SkipNumberFast(s, 0)
+	// has error or trailing chars
+	if !ok || end != len(s) {
+		return error_mismatch(node, ctx, jsonNumberType)
+	}
+
+	*(*json.Number)(vp) = json.Number(num)
+	return nil
+}

+ 62 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/structs.go

@@ -0,0 +1,62 @@
+package optdec
+
+import (
+	"reflect"
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/decoder/consts"
+	caching "github.com/bytedance/sonic/internal/optcaching"
+	"github.com/bytedance/sonic/internal/resolver"
+)
+
+type fieldEntry struct {
+	resolver.FieldMeta
+	fieldDec decFunc
+}
+
+type structDecoder struct {
+	fieldMap   caching.FieldLookup
+	fields     []fieldEntry
+	structName string
+	typ        reflect.Type
+}
+
+func (d *structDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
+	if node.IsNull() {
+		return nil
+	}
+
+	var gerr error
+	obj, ok := node.AsObj()
+	if !ok {
+		return error_mismatch(node, ctx, d.typ)
+	}
+
+	next := obj.Children()
+	for i := 0; i < obj.Len(); i++ {
+		key, _ := NewNode(next).AsStrRef(ctx)
+		val := NewNode(PtrOffset(next, 1))
+		next = val.Next()
+
+		// find field idx
+		idx := d.fieldMap.Get(key, ctx.Options()&uint64(consts.OptionCaseSensitive) != 0)
+        if idx == -1 {
+            if Options(ctx.Options())&OptionDisableUnknown != 0 {
+                return error_field(key)
+            }
+            continue
+        }
+
+		offset := d.fields[idx].Path[0].Size
+		elem := unsafe.Pointer(uintptr(vp) + offset)
+		err := d.fields[idx].fieldDec.FromDom(elem, val, ctx)
+
+		// deal with mismatch type errors
+		if gerr == nil && err != nil {
+			// TODO: better error info
+			gerr = err
+		}
+	}
+	return gerr
+}
+

+ 60 - 0
vendor/github.com/bytedance/sonic/internal/decoder/optdec/types.go

@@ -0,0 +1,60 @@
+/*
+ * Copyright 2021 ByteDance Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package optdec
+
+import (
+	"encoding"
+	"encoding/base64"
+	"encoding/json"
+	"reflect"
+	"unsafe"
+
+	"github.com/bytedance/sonic/internal/rt"
+)
+
+var (
+	boolType                = reflect.TypeOf(bool(false))
+	byteType                = reflect.TypeOf(byte(0))
+	intType                 = reflect.TypeOf(int(0))
+	int8Type                = reflect.TypeOf(int8(0))
+	int16Type               = reflect.TypeOf(int16(0))
+	int32Type               = reflect.TypeOf(int32(0))
+	int64Type               = reflect.TypeOf(int64(0))
+	uintType                = reflect.TypeOf(uint(0))
+	uint8Type               = reflect.TypeOf(uint8(0))
+	uint16Type              = reflect.TypeOf(uint16(0))
+	uint32Type              = reflect.TypeOf(uint32(0))
+	uint64Type              = reflect.TypeOf(uint64(0))
+	float32Type             = reflect.TypeOf(float32(0))
+	float64Type             = reflect.TypeOf(float64(0))
+	stringType              = reflect.TypeOf("")
+	bytesType               = reflect.TypeOf([]byte(nil))
+	jsonNumberType          = reflect.TypeOf(json.Number(""))
+	base64CorruptInputError = reflect.TypeOf(base64.CorruptInputError(0))
+	anyType                 = rt.UnpackType(reflect.TypeOf((*interface{})(nil)).Elem())
+)
+
+var (
+	errorType                   = reflect.TypeOf((*error)(nil)).Elem()
+	jsonUnmarshalerType         = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
+	encodingTextUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
+)
+
+func rtype(t reflect.Type) (*rt.GoItab, *rt.GoType) {
+	p := (*rt.GoIface)(unsafe.Pointer(&t))
+	return p.Itab, (*rt.GoType)(p.Value)
+}

+ 0 - 111
vendor/github.com/bytedance/sonic/internal/decoder/stubs_go116.go

@@ -1,111 +0,0 @@
-// +build go1.16,!go1.20
-
-/*
- * Copyright 2021 ByteDance Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package decoder
-
-import (
-    `unsafe`
-    `reflect`
-
-    _ `github.com/cloudwego/base64x`
-
-    `github.com/bytedance/sonic/internal/rt`
-)
-
-//go:linkname _subr__b64decode github.com/cloudwego/base64x._subr__b64decode
-var _subr__b64decode uintptr
-
-// runtime.maxElementSize
-const _max_map_element_size uintptr = 128
-
-func mapfast(vt reflect.Type) bool {
-    return vt.Elem().Size() <= _max_map_element_size
-}
-
-//go:nosplit
-//go:linkname throw runtime.throw
-//goland:noinspection GoUnusedParameter
-func throw(s string)
-
-//go:linkname convT64 runtime.convT64
-//goland:noinspection GoUnusedParameter
-func convT64(v uint64) unsafe.Pointer
-
-//go:linkname convTslice runtime.convTslice
-//goland:noinspection GoUnusedParameter
-func convTslice(v []byte) unsafe.Pointer
-
-//go:linkname convTstring runtime.convTstring
-//goland:noinspection GoUnusedParameter
-func convTstring(v string) unsafe.Pointer
-
-//go:noescape
-//go:linkname memequal runtime.memequal
-//goland:noinspection GoUnusedParameter
-func memequal(a unsafe.Pointer, b unsafe.Pointer, size uintptr) bool
-
-//go:noescape
-//go:linkname memmove runtime.memmove
-//goland:noinspection GoUnusedParameter
-func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
-
-//go:linkname mallocgc runtime.mallocgc
-//goland:noinspection GoUnusedParameter
-func mallocgc(size uintptr, typ *rt.GoType, needzero bool) unsafe.Pointer
-
-//go:linkname makeslice runtime.makeslice
-//goland:noinspection GoUnusedParameter
-func makeslice(et *rt.GoType, len int, cap int) unsafe.Pointer
-
-//go:noescape
-//go:linkname growslice runtime.growslice
-//goland:noinspection GoUnusedParameter
-func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
-
-//go:linkname makemap_small runtime.makemap_small
-func makemap_small() unsafe.Pointer
-
-//go:linkname mapassign runtime.mapassign
-//goland:noinspection GoUnusedParameter
-func mapassign(t *rt.GoType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer
-
-//go:linkname mapassign_fast32 runtime.mapassign_fast32
-//goland:noinspection GoUnusedParameter
-func mapassign_fast32(t *rt.GoType, h unsafe.Pointer, k uint32) unsafe.Pointer
-
-//go:linkname mapassign_fast64 runtime.mapassign_fast64
-//goland:noinspection GoUnusedParameter
-func mapassign_fast64(t *rt.GoType, h unsafe.Pointer, k uint64) unsafe.Pointer
-
-//go:linkname mapassign_fast64ptr runtime.mapassign_fast64ptr
-//goland:noinspection GoUnusedParameter
-func mapassign_fast64ptr(t *rt.GoType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer
-
-//go:linkname mapassign_faststr runtime.mapassign_faststr
-//goland:noinspection GoUnusedParameter
-func mapassign_faststr(t *rt.GoType, h unsafe.Pointer, s string) unsafe.Pointer
-
-//go:nosplit
-//go:linkname memclrHasPointers runtime.memclrHasPointers
-//goland:noinspection GoUnusedParameter
-func memclrHasPointers(ptr unsafe.Pointer, n uintptr)
-
-//go:noescape
-//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
-//goland:noinspection GoUnusedParameter
-func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)

+ 0 - 111
vendor/github.com/bytedance/sonic/internal/decoder/stubs_go120.go

@@ -1,111 +0,0 @@
-// +build go1.20
-
-/*
- * Copyright 2021 ByteDance Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package decoder
-
-import (
-    `unsafe`
-    `reflect`
-
-    _ `github.com/cloudwego/base64x`
-
-    `github.com/bytedance/sonic/internal/rt`
-)
-
-//go:linkname _subr__b64decode github.com/cloudwego/base64x._subr__b64decode
-var _subr__b64decode uintptr
-
-// runtime.maxElementSize
-const _max_map_element_size uintptr = 128
-
-func mapfast(vt reflect.Type) bool {
-    return vt.Elem().Size() <= _max_map_element_size
-}
-
-//go:nosplit
-//go:linkname throw runtime.throw
-//goland:noinspection GoUnusedParameter
-func throw(s string)
-
-//go:linkname convT64 runtime.convT64
-//goland:noinspection GoUnusedParameter
-func convT64(v uint64) unsafe.Pointer
-
-//go:linkname convTslice runtime.convTslice
-//goland:noinspection GoUnusedParameter
-func convTslice(v []byte) unsafe.Pointer
-
-//go:linkname convTstring runtime.convTstring
-//goland:noinspection GoUnusedParameter
-func convTstring(v string) unsafe.Pointer
-
-//go:noescape
-//go:linkname memequal runtime.memequal
-//goland:noinspection GoUnusedParameter
-func memequal(a unsafe.Pointer, b unsafe.Pointer, size uintptr) bool
-
-//go:noescape
-//go:linkname memmove runtime.memmove
-//goland:noinspection GoUnusedParameter
-func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
-
-//go:linkname mallocgc runtime.mallocgc
-//goland:noinspection GoUnusedParameter
-func mallocgc(size uintptr, typ *rt.GoType, needzero bool) unsafe.Pointer
-
-//go:linkname makeslice runtime.makeslice
-//goland:noinspection GoUnusedParameter
-func makeslice(et *rt.GoType, len int, cap int) unsafe.Pointer
-
-//go:noescape
-//go:linkname growslice reflect.growslice
-//goland:noinspection GoUnusedParameter
-func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
-
-//go:linkname makemap_small runtime.makemap_small
-func makemap_small() unsafe.Pointer
-
-//go:linkname mapassign runtime.mapassign
-//goland:noinspection GoUnusedParameter
-func mapassign(t *rt.GoMapType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer
-
-//go:linkname mapassign_fast32 runtime.mapassign_fast32
-//goland:noinspection GoUnusedParameter
-func mapassign_fast32(t *rt.GoMapType, h unsafe.Pointer, k uint32) unsafe.Pointer
-
-//go:linkname mapassign_fast64 runtime.mapassign_fast64
-//goland:noinspection GoUnusedParameter
-func mapassign_fast64(t *rt.GoMapType, h unsafe.Pointer, k uint64) unsafe.Pointer
-
-//go:linkname mapassign_fast64ptr runtime.mapassign_fast64ptr
-//goland:noinspection GoUnusedParameter
-func mapassign_fast64ptr(t *rt.GoMapType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer
-
-//go:linkname mapassign_faststr runtime.mapassign_faststr
-//goland:noinspection GoUnusedParameter
-func mapassign_faststr(t *rt.GoMapType, h unsafe.Pointer, s string) unsafe.Pointer
-
-//go:nosplit
-//go:linkname memclrHasPointers runtime.memclrHasPointers
-//goland:noinspection GoUnusedParameter
-func memclrHasPointers(ptr unsafe.Pointer, n uintptr)
-
-//go:noescape
-//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
-//goland:noinspection GoUnusedParameter
-func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)

Some files were not shown because too many files changed in this diff