Skip to content

Commit

Permalink
Optimize memory usage (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexKlo authored Oct 24, 2024
1 parent 3347a20 commit 3068d38
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 16 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "GMime"
uuid = "158299e7-fc4f-4c3e-9fda-ad8bb0e07f1e"
version = "1.0.0"
version = "1.0.1"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Expand Down
9 changes: 7 additions & 2 deletions src/GMime.jl
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ export g_mime_filter_basic_new
export g_mime_content_encoding_to_string

export g_object_unref,
g_date_time_format
g_date_time_format,
g_free

export EmailAttachment,
Email,
Expand Down Expand Up @@ -282,7 +283,7 @@ function g_mime_stream_mem_new()
end

function g_mime_stream_mem_new_with_byte_array(array)
return ccall((:g_mime_stream_mem_new_with_byte_array, libgmime), Ptr{GMimeStream}, (Ptr{Vector{UInt8}},), array)
return ccall((:g_mime_stream_mem_new_with_byte_array, libgmime), Ptr{GMimeStream}, (Ptr{UInt8},), array)
end

function g_mime_stream_mem_new_with_buffer(buffer, len)
Expand Down Expand Up @@ -593,6 +594,10 @@ function g_date_time_format(datetime, format)
return ccall((:g_date_time_format, libgmime), Ptr{UInt8}, (Ptr{GDateTime}, Ptr{UInt8}), datetime, format)
end

function g_free(ptr)
return ccall((:g_free, libgmime), Cvoid, (Ptr{Cvoid},), ptr)
end

include("Parser.jl")
using .Parser

Expand Down
39 changes: 26 additions & 13 deletions src/Parser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export EmailAttachment,
using Dates
using ..GMime

const DATE_FORMAT = DateFormat("yyyy-mm-dd HH:MM:SS")

struct GMimeError <: Exception
message::String
end
Expand Down Expand Up @@ -81,16 +83,22 @@ function extract_addresses(msg::Ptr{GMimeMessage}, addr_type::GMimeAddressType)

for i = 0:size-1
addr = internet_address_list_get_address(addr_list, i)
addrs[i+1] = unsafe_string(internet_address_to_string(addr, C_NULL, true))
addr_ptr = internet_address_to_string(addr, C_NULL, true)
addrs[i+1] = unsafe_string(addr_ptr)
g_free(addr_ptr)
end

return addrs
end

function extract_date(msg::Ptr{GMimeMessage})
date = g_mime_message_get_date(msg)
date_string = unsafe_string(g_date_time_format(date, "%Y-%m-%d %H:%M:%S"))
return DateTime(date_string, DateFormat("yyyy-mm-dd HH:MM:SS"))
date_str_ptr = g_date_time_format(date, "%Y-%m-%d %H:%M:%S")
try
DateTime(unsafe_string(date_str_ptr), DATE_FORMAT)
finally
g_free(date_str_ptr)
end
end

function read_text_data(part::Ptr{GMimeObject})
Expand All @@ -106,8 +114,8 @@ function read_text_data(part::Ptr{GMimeObject})
return content
end

function handle_body(::Ptr{GMimeObject}, part::Ptr{GMimeObject}, user_data::Ptr{Vector{UInt8}})
mime_type = g_mime_object_get_content_type(part)
function handle_body(::Ptr{GMimeObject}, part::Ptr{GMimeObject}, user_data::Ptr{UInt8})
mime_type = g_mime_object_get_content_type(part)

# Skip every objects except email text body (text/plain)
g_mime_content_type_is_type(mime_type, "text", "plain") || return nothing
Expand All @@ -124,8 +132,8 @@ end

function extract_text_body(msg::Ptr{GMimeMessage})
text_body = UInt8[]
callback = @cfunction(handle_body, Cvoid, (Ptr{GMimeObject}, Ptr{GMimeObject}, Ptr{Vector{UInt8}}))
text_body_ptr = pointer_from_objref(text_body)
callback = @cfunction(handle_body, Cvoid, (Ptr{GMimeObject}, Ptr{GMimeObject}, Ptr{UInt8}))
text_body_ptr = Ptr{UInt8}(pointer_from_objref(text_body))
g_mime_message_foreach(msg, callback, text_body_ptr)
return text_body
end
Expand All @@ -141,7 +149,7 @@ function read_stream_data(stream::Ptr{GMimeStream}, buffer_size::Int64 = 4096)
return content
end

function handle_attachment(::Ptr{GMimeObject}, part::Ptr{GMimeObject}, user_data::Ptr{Vector{EmailAttachment}})
function handle_attachment(::Ptr{GMimeObject}, part::Ptr{GMimeObject}, user_data::Ptr{EmailAttachment})
mime_type = g_mime_object_get_content_type(part)

# Skip multipart objects and objects that are not attachments
Expand All @@ -166,27 +174,30 @@ function handle_attachment(::Ptr{GMimeObject}, part::Ptr{GMimeObject}, user_data

# Read attachment data
g_mime_stream_filter_add(filtered_stream, filter)
g_object_unref(filter)
attachment_data = read_stream_data(filtered_stream)
g_object_unref(filtered_stream)

# Add attachment to the list
encoding_str = unsafe_string(g_mime_content_encoding_to_string(encoding_type))
mime_type_str = unsafe_string(g_mime_content_type_get_mime_type(mime_type))
type_str_ptr = g_mime_content_type_get_mime_type(mime_type)
attachments_list = unsafe_pointer_to_objref(user_data)
push!(attachments_list, EmailAttachment(filename, encoding_str, mime_type_str, attachment_data))
push!(attachments_list, EmailAttachment(filename, encoding_str, unsafe_string(type_str_ptr), attachment_data))

g_free(type_str_ptr)
return nothing
end

function extract_attachments(msg::Ptr{GMimeMessage})
attachments = EmailAttachment[]
callback = @cfunction(handle_attachment, Cvoid, (Ptr{GMimeObject}, Ptr{GMimeObject}, Ptr{Vector{EmailAttachment}}))
attachment_ptr = pointer_from_objref(attachments)
callback = @cfunction(handle_attachment, Cvoid, (Ptr{GMimeObject}, Ptr{GMimeObject}, Ptr{EmailAttachment}))
attachment_ptr = Ptr{EmailAttachment}(pointer_from_objref(attachments))
g_mime_message_foreach(msg, callback, attachment_ptr)
return attachments
end

function stream_init(data::AbstractVector{UInt8})
stream = g_mime_stream_mem_new_with_buffer(pointer(data), length(data))
stream = g_mime_stream_mem_new_with_buffer(data, length(data))
stream == C_NULL && throw(GMimeError("Failed to create memory stream."))
return stream
end
Expand Down Expand Up @@ -268,4 +279,6 @@ function parse_email(data::AbstractString)
return parse_email(codeunits(data))
end

precompile(push!, (Vector{EmailAttachment}, EmailAttachment))

end

2 comments on commit 3068d38

@gryumov
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register()

Release notes:

  • Optimize memory usage

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/117959

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v1.0.1 -m "<description of version>" 3068d3892cf27981f0e43cffc80e3abea5697dce
git push origin v1.0.1

Please sign in to comment.