Skip to content

Commit

Permalink
[Go] Try sonic ConfigFastest
Browse files Browse the repository at this point in the history
  • Loading branch information
Alfex4936 committed Sep 28, 2024
1 parent 56d1bf6 commit 17b0e42
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 10 deletions.
3 changes: 2 additions & 1 deletion backend/handler/comment_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ func (h *CommentHandler) HandlePostComment(c *fiber.Ctx) error {
userID := c.Locals("userID").(int)
userName := c.Locals("username").(string)
var req dto.CommentRequest
if err := c.BodyParser(&req); err != nil {

if err := util.JsonBodyParser(c, &req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
}

Expand Down
8 changes: 3 additions & 5 deletions backend/handler/user_api.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package handler

import (
"log"

"github.com/Alfex4936/chulbong-kr/dto"
"github.com/Alfex4936/chulbong-kr/facade"
"github.com/Alfex4936/chulbong-kr/middleware"
Expand Down Expand Up @@ -87,14 +85,14 @@ func (h *UserHandler) HandleUpdateUser(c *fiber.Ctx) error {
// DeleteUserHandler deletes the currently authenticated user
func (h *UserHandler) HandleDeleteUser(c *fiber.Ctx) error {
userID, ok := c.Locals("userID").(int)
if !ok {
if !ok || userID == 1 { // Prevent deletion of the admin user
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "User ID not found"})
}

log.Printf("[DEBUG][HANDLER] Deleting user %v", userID)
// log.Printf("[DEBUG][HANDLER] Deleting user %v", userID)

if err := h.UserFacadeService.DeleteUserWithRelatedData(c.Context(), userID); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to delete user"})
}

return c.SendStatus(fiber.StatusNoContent) // 204 for successful deletion with no content in response
Expand Down
2 changes: 1 addition & 1 deletion backend/service/chat_conn_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ func (s *ChatService) GetAllRedisConnectionsFromRoom(markerID string) ([]dto.Con
// Use StringToBytes to avoid unnecessary memory allocation
jsonBytes := util.StringToBytes(jsonConnInfo)

if err := sonic.Unmarshal(jsonBytes, &connInfo); err != nil {
if err := sonic.ConfigFastest.Unmarshal(jsonBytes, &connInfo); err != nil {
log.Printf("Error unmarshaling connection info: %v", err)
continue
}
Expand Down
2 changes: 1 addition & 1 deletion backend/service/chat_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ func (s *ChatService) BroadcastMessage(message []byte, userID, roomID, userNickn
Timestamp: time.Now().UnixMilli(),
}
// Serialize the message struct to JSON
msgJSON, err := sonic.Marshal(broadcastMsg)
msgJSON, err := sonic.ConfigFastest.Marshal(broadcastMsg)
if err != nil {
// s.Logger.Error("Error marshalling message to JSON", zap.Error(err))
return
Expand Down
2 changes: 0 additions & 2 deletions backend/service/search_bleve_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -1085,10 +1085,8 @@ func assignTermsToFields(terms []string) []TermAssignment {
assignments := make([]TermAssignment, 0, len(terms))

for _, term := range terms {
log.Printf(" ❤️ converted to QwertyHangul %v for %s", dkssud.IsQwertyHangul(term), term)
if dkssud.IsQwertyHangul(term) {
term = dkssud.QwertyToHangul(term)
log.Printf(" ❤️ converted to QwertyHangul %s", term)
}

if util.IsProvince(term) {
Expand Down
107 changes: 107 additions & 0 deletions backend/util/fiber_util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package util

import (
"bytes"

"github.com/bytedance/sonic"
"github.com/gofiber/fiber/v2"
)

// JsonBodyParser binds the request body to a json struct with optimizations for performance.
func JsonBodyParser(c *fiber.Ctx, out interface{}) error {
// Retrieve the Content-Type header as a byte slice
contentType := c.Request().Header.ContentType()

// If Content-Type is empty, return an error
if len(contentType) == 0 {
return fiber.ErrUnprocessableEntity
}

// Convert Content-Type to lower-case in place to handle case-insensitivity
ToLower(contentType)

// Parse vendor-specific Content-Type (e.g., application/problem+json -> application/json)
parsedCType := ParseVendorSpecificContentType(contentType)

// Remove any parameters from Content-Type (e.g., application/json; charset=utf-8 -> application/json)
if semiColonIndex := bytes.IndexByte(parsedCType, ';'); semiColonIndex != -1 {
parsedCType = parsedCType[:semiColonIndex]
}

// Check if the Content-Type ends with "json"
if !bytes.HasSuffix(parsedCType, []byte("json")) {
return fiber.ErrUnprocessableEntity
}

return c.App().Config().JSONDecoder(c.Body(), out)
}

// JsonBodyParserFast binds the request body to a json struct with optimizations for performance.
// using sonic.ConfigFastest
func JsonBodyParserFast(c *fiber.Ctx, out interface{}) error {
// Retrieve the Content-Type header as a byte slice
contentType := c.Request().Header.ContentType()

// If Content-Type is empty, return an error
if len(contentType) == 0 {
return fiber.ErrUnprocessableEntity
}

// Convert Content-Type to lower-case in place to handle case-insensitivity
ToLower(contentType)

// Parse vendor-specific Content-Type (e.g., application/problem+json -> application/json)
parsedCType := ParseVendorSpecificContentType(contentType)

// Remove any parameters from Content-Type (e.g., application/json; charset=utf-8 -> application/json)
if semiColonIndex := bytes.IndexByte(parsedCType, ';'); semiColonIndex != -1 {
parsedCType = parsedCType[:semiColonIndex]
}

// Check if the Content-Type ends with "json"
if !bytes.HasSuffix(parsedCType, []byte("json")) {
return fiber.ErrUnprocessableEntity
}
return sonic.ConfigFastest.Unmarshal(c.Body(), out)
// return c.App().Config().JSONDecoder(c.Body(), out)
}

// ToLower converts an ASCII byte slice to lower-case in place.
func ToLower(b []byte) {
for i := 0; i < len(b); i++ {
if 'A' <= b[i] && b[i] <= 'Z' {
b[i] += 'a' - 'A'
}
}
}

// ParseVendorSpecificContentType efficiently parses vendor-specific content types.
// It transforms types like "application/problem+json" to "application/json".
func ParseVendorSpecificContentType(cType []byte) []byte {
plusIndex := bytes.IndexByte(cType, '+')
if plusIndex == -1 {
return cType
}

semiColonIndex := bytes.IndexByte(cType, ';')
var parsableType []byte

if semiColonIndex == -1 {
parsableType = cType[plusIndex+1:]
} else if plusIndex < semiColonIndex {
parsableType = cType[plusIndex+1 : semiColonIndex]
} else {
return cType[:semiColonIndex]
}

slashIndex := bytes.IndexByte(cType, '/')
if slashIndex == -1 {
return cType
}

// Create a new slice to hold "application/json"
parsed := make([]byte, slashIndex+1+len(parsableType))
copy(parsed, cType[:slashIndex+1])
copy(parsed[slashIndex+1:], parsableType)
return parsed
}

0 comments on commit 17b0e42

Please sign in to comment.